diff --git a/backend/.env.example b/backend/.env.example index 7c3cff3d..ca60fbbf 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -64,3 +64,9 @@ TEST_SENDER_PIN='000000' TEST_SENDER_WALLET_ADDRESS='YOUR_TEST_SENDER_WALLET' TEST_RECEIVER_WALLET_ADDRESS='YOUR_TEST_RECEIVER_WALLET' TEST_RECIPIENT_PHONE_NUMBER='+1234567890' + +# TSN settlement-token / OTDT configuration +TSN_SETTLEMENT_TOKEN_MASTER_KEY='replace-with-32-byte-base64-or-64-char-hex-secret' +TSN_CRANKER_DNA='trustlink-authorized-cranker' +TSN_LOW_LIQUIDITY_THRESHOLD='100' +TSN_RECOVERY_REWARD_BPS='200' diff --git a/docs/CRANKER.md b/docs/CRANKER.md index 6e559bf3..dee02802 100644 --- a/docs/CRANKER.md +++ b/docs/CRANKER.md @@ -1,454 +1,280 @@ -# Cranker Operators: Execution Infrastructure for TrustLink Pay +# Cranker Operators for TrustLink Pay -A Cranker is a specialized network operator that powers the TrustLink Pay Transfer Settlement Network (TSN). Crankers are the backbone of fast, private payments on Solana. +> **Update reference:** this guide reflects the TSN runtime update introduced at commit `dfa0735` (`TSN: Add settlement-token/OTDT, claim-lease & recovery runtime, and Python cranker daemon`). ---- - -## What Does a Cranker Do? - -Crankers are responsible for: +## Summary -1. **Monitoring Payment Intents** — Watch the off-chain mempool for payment intents and claim requests -2. **Acquiring Execution Leases** — Secure the exclusive right to execute a payment within an epoch -3. **Fronting Instant Payouts** — Pay recipients immediately from liquidity vaults (near-instant claims) -4. **Submitting Proof of Payment** — On-chain verification that the payment was executed -5. **Managing Epoch Reimbursements** — Receive reimbursement from escrow at epoch close +Crankers are the execution operators of the **Transfer Settlement Network (TSN)**. They verify payment intents, earn claim points, acquire claim leases, decrypt settlement tokens through One-Time Decryption Token (OTDT) controls, execute settlement work, submit proofs, and participate in smart recovery. -### The Cranker Role in Context +Crankers do not replace the identity or trust layers: -``` -Sender locks funds → Cranker monitors → Cranker pays recipient → Cranker submits proof → Epoch reimburses Cranker -``` +- **TINS** remains the payment identity layer. +- **SAS** remains the verification and attestation layer. +- **TSN** remains the private settlement layer. +- **WhatsApp** is used only for social confidence verification and notifications. -Crankers ensure transfers remain private (no direct exposure of sender/receiver wallets) while delivering fast, reliable claims. They act as reliable "keepers" that guarantee smooth settlement execution. +Crankers keep the network operational by doing useful work before they are allowed to perform settlement work. This protects liquidity, prevents duplicate claims, and keeps recoverability derived from the TSN commitment registry. --- -## Why Become a Cranker? - -### Compelling Incentives +## Cranker responsibilities -| Benefit | Description | -|---------|-------------| -| **5% Operator Share** | Earn 5% of all settlement fees for every payment you execute | -| **Real Yield** | Earnings come from actual transaction volume, not inflationary token emissions | -| **Stablecoin Adoption** | Drive adoption of your token for daily payments — the more volume, the more you earn | -| **Full Control** | Run your own node, set your own schedule, manage your own infrastructure | -| **Early Mover Advantage** | Be among the first operators as the network grows | - -### Who Should Run a Cranker? - -- **Stablecoin issuers** who want their token used for daily payments -- **Payment service providers** looking to integrate with TrustLink -- **Crypto-native operators** with infrastructure expertise -- **DeFi protocols** seeking yield on stablecoin reserves -- **Venture-backed firms** with capital to deploy for yield +| Responsibility | Description | +| --- | --- | +| Monitor intent queue | Watch TSN mempool intent work. | +| Validate intent work | Check payment intent structure, signatures where supplied, encrypted settlement-token commitment, routing, and epoch. | +| Earn claim points | Intent verification earns points that unlock later claim work. | +| Acquire claim leases | A claim lease spends claim points and grants temporary settlement execution rights. | +| Enforce OTDT | OTDT hash is written before settlement-token decryption to block duplicate claims. | +| Execute settlement | Decrypt settlement token in memory, execute settlement/payout work, and generate a settlement commitment hash. | +| Submit proof | Record proof so the commitment registry can mark the transfer recoverable. | +| Run recovery | Complete recovery jobs that return recoverable funds to liquidity and earn recovery rewards. | --- -## Compensation Structure - -Crankers earn the **operator share** of settlement fees. The current fee split: - -| Recipient | Share | Notes | -|-----------|------:|-------| -| Liquidity Providers | 87% | Vault capital that enables instant payout | -| Protocol Treasury | 8% | Protocol operations and development | -| **Cranker/Operator** | **5%** | **Your earnings** | - -### Example Earnings - -If a vault processes $1,000,000 in daily payment volume with a 0.5% settlement fee ($5,000 daily revenue): - -- **LP Share (87%)**: $4,350 → distributed to vault depositors -- **Protocol (8%)**: $400 → treasury -- **Cranker Share (5%)**: $250 → operator - -*Daily earnings scale with volume. Higher payment volume = higher cranker earnings.* - ---- - -## Launch Strategy: Phased Rollout - -At launch, **TrustLink Pay will be the first and primary cranker operator**. Running a cranker will not be open to everyone initially. This ensures high reliability, speed, and security while the network proves itself. - -### Phase 1: TrustLink as Primary Cranker (Now → TBD) - -- TrustLink deploys and operates cranker nodes on high-performance infrastructure -- TrustLink funds or partners with firms to fund token-specific vaults -- Users experience fast, reliable settlement from day one -- Stablecoin issuers can participate as LPs (see [LIQUIDITY.md](./LIQUIDITY.md)) - -### Phase 2: Open Operator Network (TBD → Future) - -As the network proves itself with growing volume: - -- Additional verified operators are welcomed -- Firms can deploy their own cranker nodes -- Competition improves resilience and reduces settlement times -- LP yields potentially increase due to greater vault availability - -### How to Position for Phase 2 - -If you're interested in running a cranker when the network opens: - -1. **Start as an LP** — Fund a vault, earn yields, understand the system -2. **Build infrastructure** — Set up a server, configure the cranker daemon -3. **Engage with TrustLink** — Join Discord, express interest, get updates -4. **Monitor network growth** — Track volume, vault utilization, and operator demand - ---- - -## Technical Requirements - -### Hardware Requirements - -| Component | Minimum | Recommended | -|-----------|---------|-------------| -| CPU | 4 cores | 8+ cores | -| RAM | 8 GB | 16+ GB | -| Storage | 100 GB SSD | 256 GB+ NVMe SSD | -| Network | 100 Mbps | 1 Gbps | -| Uptime | 99% | 99.9% | - -### Software Requirements - -- **Node.js 20+** — Runtime for the cranker daemon -- **Redis** — State management and caching -- **Solana CLI** — Blockchain interaction -- **tsn-cranker-sdk** — TrustLink's operator SDK - -### Network Requirements - -- **Static IP** — For reliable node discovery -- **Firewall** — Allow incoming connections on specified ports -- **DNS** — Optional, for human-readable node addresses +## End-to-end operator flow + +```text +1. TINS resolves recipient identity. +2. TSN creates a payment intent and encrypted settlement-token commitment. +3. Cranker verifies intent work. +4. Cranker receives claim points. +5. Claim points unlock a claim lease. +6. Claim lease issues an OTDT hash. +7. Cranker decrypts the settlement token in memory. +8. Cranker executes settlement work and submits proof. +9. Commitment registry marks the transfer recoverable. +10. Recovery queue receives open work. +11. Smart recovery prioritizes jobs based on liquidity pressure. +12. Eligible Cranker completes recovery and returns liquidity. +``` --- -## Setting Up Your Cranker Node - -### Step 1: Install Dependencies - -```bash -# Install Node.js 20+ -curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - -sudo apt-get install -y nodejs - -# Install Redis -sudo apt-get install redis-server -sudo systemctl enable redis - -# Install Solana CLI -sh -c "$(curl -sSfL "https://release.solana.com/stable/install.sh")" -``` +## Claim points and claim leases -### Step 2: Clone and Configure SDK +Crankers must complete intent work before they can perform settlement work. -```bash -# Clone the repository -git clone https://github.com/trustlink/trustlink-pay.git -cd trustlink-pay/tsn-cranker-sdk -npm install - -# Configure environment -cat > .env << EOF -WALLET_PRIVATE_KEY=your_base58_private_key -REDIS_URL=redis://localhost:6379 -VERIFIER_PDA=your_verifier_pda_address -TSN_PROGRAM_ID=your_tsn_program_id -NETWORK=devnet -LOG_LEVEL=info -EOF +```text +intent verification -> +1 claim point +claim lease -> -1 available claim point ``` -### Step 3: Register Your Cranker - -Before running, you must register as a verified cranker: - -```bash -npm run register \ - -- --wallet-key "$WALLET_PRIVATE_KEY" \ - --network devnet -``` +A claim lease is a temporary right to settle one transfer. It exists so the network can prevent a Cranker from repeatedly attempting the same settlement or racing a duplicate proof after a transfer is already recoverable. -Registration requires: -- Valid Solana wallet -- Sufficient SOL for registration fees -- Agreement to operator terms +A claim lease is refused when: -### Step 4: Fund Your Vault +- the commitment registry entry does not exist; +- the transfer is already recoverable; +- an OTDT hash already exists; +- the Cranker has no available claim points. -Crankers need access to vault liquidity to front payouts. See [LIQUIDITY.md](./LIQUIDITY.md) for vault funding details. +--- -### Step 5: Start Your Node +## OTDT enforcement -```bash -# Start the cranker daemon -npm run cranker start +OTDT means **One-Time Decryption Token**. In this implementation, the runtime stores an OTDT hash in the commitment registry before settlement-token decryption. The actual token secret is not written to the registry. -# Check status -npm run cranker status +The OTDT sequence is: -# View metrics -npm run cranker stats +```text +check registry recoverable=false +check registry otdtHash is empty +spend claim point +create claim lease +create OTDT hash +write otdtHash to registry +decrypt settlement token in Cranker memory +submit settlement proof with matching OTDT hash ``` -### Step 6: Monitor Performance - -Access the dashboard at `http://localhost:3002/dashboard` to view: - -- Processed intents (payments executed) -- Pending intents (awaiting lease) -- Reimbursed amounts (epoch settlements) -- Error rates and latency -- AI protection alerts -- Cranker jail status +If the registry already has `recoverable=true` or `otdtHash`, the Cranker must not decrypt or settle the transfer again. --- -## Fraud Protection for Crankers +## Commitment registry privacy boundary -TSN Mempool includes fraud protection that monitors all cranker operations. The system is designed to detect malicious behavior without monitoring normal operator activity. +The commitment registry is public settlement-verification state. It stores transfer commitments, not private settlement data. -### Monitoring of Cranker Operations +### The registry stores -| Operation | Fraud Protection | Notes | -|-----------|------------------|-------| -| Lease acquisition | Fraud detection | Detects front-running patterns | -| Payout execution | Proof verification | Validates amounts and signatures | -| Proof submission | Settlement protection | Prevents double-spend attempts | -| Epoch reimbursement | Anomaly detection | Guards against manipulation | +- transfer ID +- encrypted settlement token +- commitment hash of the decrypted token +- timestamp +- epoch +- recoverable flag +- intent verifier pubkey +- settlement proof reference +- settlement commitment hash +- OTDT hash +- recovery proof reference -### Cranker Jail System +### The registry must not store -The fraud protection cranker jail system monitors crankers for malicious behavior: +- sender wallet +- recipient main wallet +- decrypted settlement token +- phone number +- WhatsApp identifier +- token balances +- SAS PII or raw attestation payloads -``` -Trust Score: 1.0 (100%) - │ - ▼ Violation detected -Trust Score: 0.9 - │ - ▼ Violation detected -Trust Score: 0.8 - │ - ▼ (trust < 0.3) OR (violations >= 3) - ▼ - JAILED (1 hour minimum) - │ - ▼ After jail period - RELEASED (trust reset to 0.5) - │ - ▼ Additional violations - BANNED (permanent) -``` - -### Jail Reasons & Trust Impact - -| Reason | Severity | Trust Impact | -|--------|----------|--------------| -| Fraudulent proofs | Critical | -20% per violation | -| Payout manipulation | High | -15% per violation | -| Proof withholding | High | -15% per violation | -| Front-running | Medium | -12% per violation | -| Failed obligations | Low | -10% per violation | -| Sybil attack | Critical | -20% per violation | - -### Avoiding Jail - -- Submit valid proofs for every payout -- Never manipulate payout amounts -- Respond within epoch timeframes -- Don't engage in front-running -- Maintain consistent operation patterns +This privacy boundary is central to TrustLink Pay. A Cranker may need operational data to complete settlement, but that data must not be written into public registry state. --- -## Cranker Commands Reference +## Smart recovery -| Command | Description | -|---------|-------------| -| `start` | Begin processing payment intents | -| `stop` | Graceful shutdown | -| `status` | Check node health and connectivity | -| `stats` | View performance metrics | -| `pause` | Pause intent processing (maintenance) | -| `resume` | Resume after pause | +After settlement proof is accepted, TSN derives recovery state from the commitment registry and enqueues recovery work. Recovery jobs are competitive open work. ---- - -## Understanding Execution Leases - -### What is a Lease? +Smart recovery considers: -An execution lease is an exclusive right to execute a specific payment within an epoch. Only one cranker can hold a lease for a payment at any time. +- active liquidity +- pending intent demand +- vault balance +- settlement velocity +- liquidity consumption rate +- time and priority pressure through the queue score -### Lease Acquisition - -```typescript -// Monitoring for lease opportunities -while (true) { - const intents = await mempool.getPendingIntents(); - for (const intent of intents) { - if (!intent.hasLease) { - const acquired = await tryAcquireLease(intent.id); - if (acquired) { - await executePayment(intent); - } - } - } - await sleep(100); // Poll interval -} -``` +A recovery job records: -### Lease Exclusivity +- transfer ID +- epoch +- recoverable amount +- vault source +- recovery reward +- priority score +- lease/completion status +- recovery proof reference -- Only registered/verified crankers can acquire leases -- Lease state is atomic — prevents double execution -- Failed lease attempts indicate competition (good for network health) +The priority score increases when liquidity falls below the configured threshold or pending settlement demand is high. This helps depleted Crankers and liquidity pools recover operational capacity faster. --- -## Epoch Settlement: How You Get Paid Back +## TypeScript Cranker/runtime components -Crankers front payouts from vault liquidity. At epoch close, the Mother Escrow reimburses crankers from sender escrow funds. +The TypeScript TSN SDK provides the mempool and API surface used by Crankers and backend services. -### Epoch Cycle (Currently ~7 Hours) +Key files: -1. **Payment Executed** — Cranker pays recipient from vault -2. **Proof Submitted** — Cranker submits on-chain proof -3. **Epoch Closes** — Mother Escrow processes reimbursements -4. **Vault Replenished** — Cranker's vault is credited for reimbursement -5. **Fees Distributed** — Settlement fees distributed per split +| File | Role | +| --- | --- | +| `tsn-sdk/src/settlement-token.ts` | Settlement-token encryption/decryption, commitment hashes, Cranker DNA hash, OTDT hash helper. | +| `tsn-sdk/src/mempool.ts` | JSON mempool, commitment registry, claim points, claim leases, proof acceptance, recovery queue, liquidity metrics. | +| `tsn-sdk/src/server.ts` | HTTP API around the mempool runtime. | +| `tsn-sdk/src/contracts.ts` | Public TSN types for intents, registry entries, leases, recovery jobs, metrics, and proofs. | -### Reimbursement Flow +Useful operations: +```ts +await tsn.postIntent(intentRequest); +await tsn.submitIntentVerification(intentId, crankerPubkey); +await tsn.acquireClaimLease(intentId, crankerPubkey); +await tsn.postProof(proofRequest); +await tsn.listRecoveryQueue({ status: "open" }); +await tsn.completeRecoveryJob(jobId, crankerPubkey, proofTx); ``` -Sender Escrow → Mother Escrow → Cranker Vault → (Fee Split Applied) - ↓ - 87% to LP depositors - 5% to Cranker (you) - 8% to Protocol Treasury -``` - -### Managing Capital at Risk - -| Factor | Impact | -|--------|--------| -| Vault Size | Larger vault = more lease capacity | -| Epoch Length | Longer epochs = more capital at risk between reimbursements | -| Payment Volume | Higher volume = faster capital turnover | -| Network Latency | Slower proof submission = delayed reimbursement | - -**Tip**: Monitor your vault balance and epoch timing. A depleted vault means missed lease opportunities. --- -## Troubleshooting - -### "Unable to acquire lease" +## Python Cranker daemon -**Cause**: Another cranker was faster -**Solutions**: -- Increase monitoring frequency (reduce poll interval) -- Optimize network latency to mempool -- Accept that some intents will be competitive +The Python daemon is provided for autonomous queue processing and operator automation. -### "Insufficient vault funds" +Path: -**Cause**: Vault balance is too low to front payout -**Solutions**: -- Check LP depositor activity -- Add more liquidity to vault -- Wait for epoch reimbursement - -### "Verifier funds low" - -**Cause**: Verifier PDA has insufficient SOL for account creation -**Solutions**: -- Contact TrustLink to top up verifier PDA -- If you're a registered operator, fund your own verifier +```text +tsn-cranker-op-daemon/scripts/cranker_daemon.py +``` -### "Redis connection failed" +### Run once -**Cause**: Redis server not running or unreachable -**Solutions**: ```bash -# Check Redis status -sudo systemctl status redis +TSN_SETTLEMENT_TOKEN_MASTER_KEY=<32-byte-base64-or-64-char-hex-secret> \ +TSN_CRANKER_ONCE=true \ +python tsn-cranker-op-daemon/scripts/cranker_daemon.py +``` -# Restart Redis -sudo systemctl restart redis +### Run continuously -# Verify REDIS_URL in environment -echo $REDIS_URL +```bash +TSN_SETTLEMENT_TOKEN_MASTER_KEY=<32-byte-base64-or-64-char-hex-secret> \ +npm --prefix tsn-cranker-op-daemon run crank:python ``` -### "Proof submission failed" +### Python daemon tasks -**Cause**: Network issue or epoch state inconsistency -**Solutions**: -- Check Solana network status -- Verify your wallet has sufficient SOL for transaction fees -- Review cranker daemon logs for specific error +1. Load the TSN mempool file. +2. Verify pending intents. +3. Award claim points. +4. Acquire leases for eligible claims. +5. Write OTDT hashes. +6. Decrypt settlement tokens in memory. +7. Submit settlement proof references. +8. Mark registry entries recoverable. +9. Enqueue recovery work. +10. Complete priority recovery jobs. +11. Update liquidity metrics. --- -## Best Practices +## Configuration -### Infrastructure - -- **Run redundant nodes** — Multiple cranker instances improve reliability -- **Use load balancers** — Distribute intent monitoring across instances -- **Monitor health continuously** — Set up alerts for node downtime -- **Keep software updated** — Update cranker SDK when new versions release - -### Capital Management - -- **Maintain vault buffer** — Keep 20% extra liquidity above expected needs -- **Monitor epoch timing** — Replenish vault before epoch close -- **Track utilization** — Adjust vault size based on lease demand - -### Security - -- **Protect private keys** — Use hardware wallets or secure key management -- **Rotate keys periodically** — Regular key rotation reduces risk -- **Monitor for unauthorized access** — Review access logs +| Variable | Required | Description | +| --- | --- | --- | +| `TSN_SETTLEMENT_TOKEN_MASTER_KEY` | Yes | Secret used to derive settlement-token encryption/decryption keys. Use deployment secrets. | +| `TSN_CRANKER_DNA` | Yes | Cranker DNA input; only its hash appears in encrypted token envelopes. | +| `TSN_MEMPOOL_FILE` | Local JSON mode | Path to local mempool state file. | +| `TSN_MEMPOOL_URL` | HTTP mode | URL of the TSN mempool server. | +| `TSN_LOW_LIQUIDITY_THRESHOLD` | Recommended | Minimum liquidity target for smart recovery scoring. | +| `TSN_RECOVERY_REWARD_BPS` | Recommended | Recovery reward in basis points. | +| `TSN_CRANKER_POLL_SECONDS` | Python daemon | Continuous loop interval. | --- -## Getting Involved +## Security and operating rules -### As a Stablecoin Issuer +1. Do not write sender wallets, recipient wallets, decrypted settlement tokens, phone numbers, balances, or SAS PII to the commitment registry. +2. Keep the settlement-token master key outside git and outside shared logs. +3. Treat Cranker DNA as an operator authorization secret. +4. Verify SAS attestations without storing raw private attestation payloads in TSN state. +5. Treat WhatsApp as social confidence verification only, not as the protocol identity. +6. Monitor recovery queue growth and liquidity metrics continuously. +7. Rotate secrets through a controlled operator process. +8. Keep proof generation deterministic enough for audit but private enough not to reveal wallet routing. -If you want your token to be the preferred settlement option: - -1. **Fund a vault** — Provide liquidity for your token -2. **Run a cranker** — Ensure fast execution for your token's payments -3. **Promote usage** — Users follow liquidity and speed - -### As a Liquidity Provider +--- -If you want to earn yields without running infrastructure: +## Testing notes -- See [LIQUIDITY.md](./LIQUIDITY.md) for vault funding instructions -- Your capital enables cranker operators to execute payments -- You earn 87% of settlement fees automatically +Recommended local checks: -### Partnership Inquiries +```bash +TSN_SETTLEMENT_TOKEN_MASTER_KEY= npm --prefix tsn-sdk run build +npm --prefix backend run typecheck +python -m py_compile tsn-cranker-op-daemon/scripts/cranker_daemon.py +TSN_SETTLEMENT_TOKEN_MASTER_KEY= TSN_CRANKER_ONCE=true python tsn-cranker-op-daemon/scripts/cranker_daemon.py +``` -For vault funding partnerships, cranker operator agreements, or technical discussions: +Recommended integration assertions: -- **Email**: [partnerships@trustlink.pay](mailto:partnerships@trustlink.pay) -- **Discord**: [Join our community](https://discord.gg/trustlink) -- **Twitter**: [@TrustLinkPay](https://twitter.com/TrustLinkPay) +- a new TSN intent creates a commitment registry entry; +- registry entry contains no sender wallet, recipient wallet, phone number, or decrypted token; +- intent verification awards claim points; +- claim lease acquisition writes an OTDT hash; +- duplicate lease/decryption attempts fail after OTDT issuance; +- settlement proof marks registry entry recoverable; +- recovery queue receives a job; +- recovery completion updates liquidity metrics and recovery proof reference. --- -## Related Documentation +## Related documents -- [OPERATOR.md](./OPERATOR.md) — Additional technical setup details -- [EPOCH-SETTLEMENT.md](./EPOCH-SETTLEMENT.md) — Deep dive into epoch mechanics -- [LIQUIDITY.md](./LIQUIDITY.md) — Vault funding and LP rewards -- [ARCHITECTURE.md](./ARCHITECTURE.md) — System architecture overview \ No newline at end of file +- [ARCHITECTURE.md](./ARCHITECTURE.md) +- [PROTOCOL.md](./PROTOCOL.md) +- [TINS.md](./TINS.md) +- [LIQUIDITY.md](./LIQUIDITY.md) +- [EPOCH-SETTLEMENT.md](./EPOCH-SETTLEMENT.md) +- [OTDT-SMART-RECOVERY.md](./OTDT-SMART-RECOVERY.md) diff --git a/docs/DEVELOPER.md b/docs/DEVELOPER.md index f0e586c1..9ae2798e 100644 --- a/docs/DEVELOPER.md +++ b/docs/DEVELOPER.md @@ -1,115 +1,290 @@ # TrustLink Pay Developer Guide -This guide is for developers building on TrustLink Pay, TINS, and TSN. +> **Update reference:** this guide includes the TSN runtime additions introduced at commit `dfa0735` (`TSN: Add settlement-token/OTDT, claim-lease & recovery runtime, and Python cranker daemon`). -TrustLink Pay is now documented as a TIN-first system: +## Summary + +TrustLink Pay is a TIN-first Solana payment ecosystem. Developers should think about the system as four coordinated layers: + +- **TINS (Transfer Identity Number System):** portable 10-digit payment identities and wallet abstraction. +- **SAS (Solana Attestation Service):** recipient and merchant verification through attestations without storing sensitive PII on-chain. +- **TSN (Transfer Settlement Network):** private, verifiable settlement through payment intents, Cranker verification, vaults, proofs, and recovery. +- **Crankers:** decentralized operators that verify TSN intents, perform settlement work, submit proofs, and keep liquidity healthy. + +WhatsApp is used only for social confidence verification, notifications, opt-in, and recovery communication. It is not the protocol identity. The protocol identity is the TIN. + +--- + +## Integration principles + +### Keep TINS, TSN, and SAS separated + +| Layer | Developer responsibility | +| --- | --- | +| TINS | Resolve or display the 10-digit payment identity. | +| SAS | Validate recipient/merchant trust attestations without storing PII in settlement state. | +| TSN | Create payment intents, monitor settlement state, and use SDK/mempool operations. | +| Cranker | Run verification, leases, settlement proofing, and recovery operations. | + +Do not collapse these layers into one application-specific flow. A payment may use all layers, but each layer has a distinct purpose. + +### Use SDK-owned operations + +Application code should not manually recreate protocol internals. Prefer SDK calls and protocol-owned helpers. + +Good examples: + +```ts +await tins.resolveIdentity(recipientTin); +await tsn.postIntent(intentRequest); +await tsn.submitIntentVerification(intentId, crankerPubkey); +await tsn.acquireClaimLease(intentId, crankerPubkey); +``` + +Avoid: + +```ts +// Avoid hand-rolling PDA, settlement-token, or lease logic in product code. +const pda = deriveSomeInternalAddressByCopyingProtocolSeeds(); +``` + +--- + +## Payment identity flow + +A developer-facing TrustLink Pay send flow should read as: ```text -10-digit TIN -> TSN private settlement -> recipient payout +recipient TIN + -> TINS identity resolution + -> optional SAS trust check + -> TSN payment intent + -> Cranker settlement and proof ``` -Phone and WhatsApp flows may exist in the app for notifications and optional linking, but integrations should treat the TIN as the primary payment identity. +Do not present raw wallet addresses as the normal receive identity. Wallets may exist behind TINS and TSN routing, but the user-facing identity is the TIN. --- -## Integration Rules +## TSN payment-intent flow -### Use The SDK +A TSN intent now includes an encrypted settlement token and registry commitment. -Protocol-specific work belongs in the SDK. +```text +create intent + -> generate encrypted settlement token + -> hash decrypted token into commitment hash + -> store encrypted token + commitment hash in registry + -> leave sender wallet, recipient wallet, balances, and plaintext out of registry +``` -Applications should not manually: +Example using the local JSON mempool runtime: -- derive TSN PDAs, -- assemble TSN instructions, -- build settlement transactions, -- serialize account layouts, -- reproduce cranker logic. +```ts +import { JsonFileTsnMempool } from "@trustlink/tsn-sdk/mempool"; +import { buildCreateIntentRequest } from "@trustlink/tsn-sdk/contracts"; -Applications should: +const tsn = new JsonFileTsnMempool(); -- collect user input, -- connect wallets, -- call SDK methods, -- request wallet signatures, -- display status. +const intent = await tsn.postIntent( + buildCreateIntentRequest({ + paymentId, + recipientHash, + tokenMintAddress, + amount, + source: "trustlink-pay", + }), +); +``` -### Keep TIN First +If `encryptedSettlementToken` and `settlementTokenCommitmentHash` are not supplied, the runtime generates them before storing the intent. -New integrations should ask for a TIN, not a phone number. +--- -Good: +## Commitment registry rules + +The commitment registry is the single source of truth for recoverability and settlement verification. + +### Allowed registry data + +- transfer ID +- encrypted settlement token +- commitment hash +- timestamp +- epoch +- recoverable flag +- intent verifier pubkey +- settlement proof reference +- settlement commitment hash +- OTDT hash +- recovery proof reference + +### Forbidden registry data + +- sender wallet +- recipient main wallet +- decrypted token +- phone number +- WhatsApp identifier +- wallet balances +- SAS PII or raw attestation contents + +If a feature requires private routing information, keep it outside the registry and only expose the minimum proof material required for TSN auditability. + +--- + +## Intent work, claim points, and claim leases + +Crankers must perform intent work before settlement work. ```text -recipientTin: "1000000008" +pending intent + -> Cranker validates structure/signatures/routing/epoch + -> intent becomes escrowed + -> Cranker earns claim point + -> claim point can be spent on a claim lease ``` -Avoid positioning this as the primary product: +A claim lease is required before settlement. A Cranker cannot settle a claim without a lease. -```text -recipientPhone: "+234..." +```ts +await tsn.submitIntentVerification(intent.id, crankerPubkey); +const lease = await tsn.acquireClaimLease(intent.id, crankerPubkey); ``` -Phone/social linking can be added later as optional discovery. +The lease writes an OTDT hash into the commitment registry. If the registry already has `recoverable=true` or an `otdtHash`, duplicate settlement must stop. --- -## Daily Use Cases +## OTDT and settlement-token decryption -### Wallet Receive Privacy +OTDT means **One-Time Decryption Token**. It is the control that gates settlement-token decryption. -A wallet can let users share a TIN instead of exposing a raw address. Payments route through TSN so the normal payment path does not reveal a simple sender-to-recipient wallet graph. +Runtime sequence: -### Merchant Payments +```text +check registry for existing recoverable proof +check registry for existing OTDT hash +spend claim point +create claim lease +write OTDT hash +decrypt settlement token in Cranker memory +submit proof +mark registry recoverable +``` -A merchant can publish a TIN as its receive identity. Customers pay the TIN, while treasury wallets remain behind the settlement layer. +The decrypted token is never stored in the registry or docs examples. It should remain in operator memory for the shortest possible time. -### Stablecoin Transfers +--- + +## Recovery and liquidity operations -Users can send approved stablecoins to TINs with cranker-sponsored settlement and recipient payout. +Once settlement proof is accepted, TSN marks the registry entry recoverable and creates recovery work. -### Future Payment PDA Flow +Recovery jobs contain: -A future receive surface may let users generate payment PDAs. Funds entering that PDA can be detected and routed through TSN, then auto-claimed according to recipient settings. +- transfer ID +- epoch +- recoverable amount +- vault source +- recovery reward +- priority score +- status + +Smart recovery prioritizes jobs using active liquidity, pending intents, vault balance, settlement velocity, and liquidity consumption rate. + +```ts +const jobs = await tsn.listRecoveryQueue({ status: "open" }); +await tsn.completeRecoveryJob(jobs[0].id, crankerPubkey, recoveryProofTx); +``` --- -## Security Considerations +## Python and TypeScript component map -| Risk | Mitigation | -| --- | --- | -| Address exposure | TIN-first receive identity and TSN settlement separation | -| Tampered mempool work | Cranker validates authorization and transaction structure | -| Replay attacks | Nonce and expiry in sender authorization | -| Competing claim execution | Claim credit and cranker coordination | -| Stuck escrow | Claim/retry surfaces and status tracking | -| Misleading UX | Sender view shows escrowed, recipient view shows claim state | +| Component | Language | Purpose | +| --- | --- | --- | +| `tsn-sdk/src/settlement-token.ts` | TypeScript | Settlement-token encryption/decryption, commitments, Cranker DNA hash, OTDT helper. | +| `tsn-sdk/src/mempool.ts` | TypeScript | JSON mempool, registry, claim points, claim leases, recovery queue, liquidity metrics. | +| `tsn-sdk/src/server.ts` | TypeScript | HTTP API for mempool operations. | +| `tsn-cranker-op-daemon/scripts/cranker_daemon.py` | Python | Autonomous queue scheduler for intent work, settlement work, and recovery. | --- -## Architecture Summary +## Local development examples -```text -User enters TIN -SDK prepares TSN authorization -Sender signs authorization/co-signed settlement payload -Mempool stores pending work -Cranker verifies and sponsors escrow -Funds enter TSN vault path -Recipient claim work becomes available -Cranker executes payout -Proof is stored through tx hashes and mempool state +### Build TSN SDK + +```bash +TSN_SETTLEMENT_TOKEN_MASTER_KEY= npm --prefix tsn-sdk run build ``` +### Run backend typecheck + +```bash +npm --prefix backend run typecheck +``` + +### Compile Python daemon + +```bash +python -m py_compile tsn-cranker-op-daemon/scripts/cranker_daemon.py +``` + +### Run Python Cranker once + +```bash +TSN_SETTLEMENT_TOKEN_MASTER_KEY= \ +TSN_CRANKER_ONCE=true \ +python tsn-cranker-op-daemon/scripts/cranker_daemon.py +``` + +--- + +## Security and privacy checklist + +- [ ] Use TINS as the payment identity. +- [ ] Validate SAS attestations without storing sensitive PII in TSN state. +- [ ] Use WhatsApp only for social confidence verification and notifications. +- [ ] Never store sender wallet in the commitment registry. +- [ ] Never store recipient wallet in the commitment registry. +- [ ] Never store decrypted settlement tokens in the commitment registry. +- [ ] Keep `TSN_SETTLEMENT_TOKEN_MASTER_KEY` in deployment secrets. +- [ ] Require claim leases before settlement work. +- [ ] Require OTDT hash before settlement-token decryption. +- [ ] Derive recoverability from registry proof state, not separate claimed flags. +- [ ] Treat confidential transfer / TF-token flows as conceptual until separately tested and shipped. + --- -## Developer Checklist +## Testing notes + +Minimum checks for TSN runtime work: + +```bash +TSN_SETTLEMENT_TOKEN_MASTER_KEY= npm --prefix tsn-sdk run build +npm --prefix backend run typecheck +python -m py_compile tsn-cranker-op-daemon/scripts/cranker_daemon.py +``` + +Recommended integration checks: + +1. Create TSN intent. +2. Confirm encrypted settlement token and commitment hash exist. +3. Confirm commitment registry excludes wallets, phone numbers, balances, and plaintext. +4. Verify intent and confirm claim points increase. +5. Acquire claim lease and confirm OTDT hash appears. +6. Submit settlement proof and confirm `recoverable=true`. +7. Confirm recovery queue entry is created. +8. Complete recovery and confirm liquidity metrics update. + +--- -- [ ] Display TIN as the payment identity. -- [ ] Do not expose raw wallet addresses as the normal receive flow. -- [ ] Use the TSN SDK for settlement construction. -- [ ] Use the TINS SDK for identity resolution. -- [ ] Show payment status as pending, escrowed, claiming, executed, canceled, or retryable. -- [ ] Keep phone/social linking optional and application-specific. +## Related documents -For questions: `security@trustlink.pay` +- [ARCHITECTURE.md](./ARCHITECTURE.md) +- [PROTOCOL.md](./PROTOCOL.md) +- [TINS.md](./TINS.md) +- [CRANKER.md](./CRANKER.md) +- [LIQUIDITY.md](./LIQUIDITY.md) +- [OTDT-SMART-RECOVERY.md](./OTDT-SMART-RECOVERY.md) diff --git a/docs/FRONTEND-REFACTOR-MAP.md b/docs/FRONTEND-REFACTOR-MAP.md new file mode 100644 index 00000000..386e1bfd --- /dev/null +++ b/docs/FRONTEND-REFACTOR-MAP.md @@ -0,0 +1,125 @@ +# Frontend Modular Refactor Map — Send Experience + +> **Update reference:** introduced after commit `64ddea2` to begin converting large React experience files into focused TrustLink Pay modules while preserving the current TL/TINS/TSN visual language. + +## Summary + +This refactor starts with the TrustLink Pay **Send** experience because it is one of the largest front-end files and contains mixed rendering, formatting, TINS recipient parsing, WhatsApp social-confidence resolution, TSN settlement review state, and modal UI. The goal is to keep the existing UI/UX intact while moving reusable code into files that match their purpose. + +The front-end still follows our protocol boundaries: + +- **TINS** identity input and 10-digit TIN parsing remain in the send flow. +- **SAS** verification remains a separate trust layer and is not mixed with TSN settlement UI. +- **TSN** settlement review and Cranker-sponsored send behavior remain in TSN-owned panels. +- **Cranker**, **OTDT**, settlement-token, and mempool runtime details remain settlement concepts and are not exposed as raw wallet data in the recipient identity UI. + +No new Tailwind classes, colors, or visual patterns were introduced. Existing TL/TINS/TSN classes were preserved. + +--- + +## Refactored file map + +```text +frontend/src/components/experiences/send-experience.tsx + Send page coordinator. Owns page-local state, API orchestration, wallet connection, + TINS recipient resolution, TSN settlement submission, and composition of child UI. + +frontend/src/components/experiences/send/types.ts + Shared send-experience types: + - SendFormState + - SendSuccessState + - PhoneVerificationDetails + - RecipientVerificationState + - ResolvedRecipientLookup + +frontend/src/components/experiences/send/components/CountrySearchModal.tsx + Focused modal for country search and country selection. + Contains only modal rendering and modal-local event forwarding. + +frontend/src/components/experiences/send/components/SendSuccessPanel.tsx + Focused post-send receipt panel for TSN intent success, WhatsApp receipt state, + invite sharing, and “send another” action rendering. + +frontend/src/components/experiences/send/utils/formatting.ts + Reusable send formatting helpers: + - paymentStatusLabel + - formatTokenBalance + - formatReceiptTime + +frontend/src/components/experiences/send/utils/tin-input.ts + TINS input helpers: + - normalizeTinInput + - looksLikeTinCandidate + +frontend/src/components/experiences/send/utils/reset-recipient-resolution.ts + Shared recipient-resolution reset helper for clearing TINS/WhatsApp preview state. +``` + +--- + +## Implementation notes + +### Component boundaries + +`SendExperience` now imports focused components for: + +- country selection modal rendering; +- send-success receipt rendering; +- send-specific formatting; +- TINS candidate parsing; +- recipient-resolution reset behavior. + +The extracted components are intentionally small. They receive state and callbacks from the page coordinator instead of reaching into global state or duplicating TSN/TINS logic. + +### TINS input handling + +TIN parsing moved into `send/utils/tin-input.ts`. This keeps the parsing and check-digit logic reusable while leaving the `SendExperience` component responsible only for deciding when to call it. + +### Country search modal + +The country search modal moved into its own component file. It preserves the existing modal layout, TL field styling, country list behavior, and active-country highlight. + +### Send success panel + +The send success panel moved into its own component file. It preserves: + +- TSN intent success messaging; +- WhatsApp receipt display; +- manual invite display; +- invite sharing; +- “Back home” and “Send another” actions. + +The panel does not expose raw wallets or balances. It continues to mask transaction-like references through existing UI behavior. + +--- + +## Security and privacy considerations + +- The refactor does not add new wallet, balance, phone, or settlement-token display surfaces. +- TINS identity input remains the public payment identity surface. +- WhatsApp remains the only social-confidence channel. +- TSN settlement state remains in the send/settlement review area and is not mixed into unrelated identity panels. +- The UI continues to avoid displaying raw recipient wallet addresses. +- Confidential transfer / TF-token flows remain conceptual and are not wired to live token accounts. + +--- + +## Testing notes + +The intended checks for this refactor are: + +```bash +npm --prefix frontend run typecheck +``` + +In the current container, frontend dependencies are not installed and `npm --prefix frontend install` is blocked by an npm registry `403` for `@solana/spl-token`, so typechecking cannot complete in this environment. The refactor was kept to import-safe TypeScript/React module extraction with existing class names preserved. + +Manual review checklist: + +1. Open the Send page. +2. Verify TINS and WhatsApp recipient entry still resolves. +3. Open and close the country selector. +4. Select a country and confirm the previous retry behavior remains. +5. Review a TSN send. +6. Confirm the success panel still displays receipt state and invite sharing. +7. Confirm no raw recipient wallet, sender wallet, balance, phone registry data, OTDT, or decrypted settlement-token material is newly exposed. diff --git a/docs/OTDT-SMART-RECOVERY.md b/docs/OTDT-SMART-RECOVERY.md new file mode 100644 index 00000000..e9a909ea --- /dev/null +++ b/docs/OTDT-SMART-RECOVERY.md @@ -0,0 +1,458 @@ +# TSN Settlement Tokens, OTDT, Claim Leases, and Smart Recovery + +> **Update reference:** introduced in the TSN runtime update at commit `dfa0735` (`TSN: Add settlement-token/OTDT, claim-lease & recovery runtime, and Python cranker daemon`). This document is the protocol-facing guide for that update. + +## Summary + +TrustLink Pay is built from three protocol layers and one operator layer: + +- **TINS (Transfer Identity Number System)** gives users portable 10-digit payment identities and wallet abstraction. +- **SAS (Solana Attestation Service)** supplies trust credentials and verification results without placing personally identifiable information on-chain. +- **TSN (Transfer Settlement Network)** performs private, verifiable settlement through payment intents, escrow/vault accounting, Cranker verification, proof records, and recovery. +- **Crankers** are decentralized operators that verify intents, sponsor or execute settlement work, submit proofs, and keep TSN liquidity operational. + +The `dfa0735` runtime update adds a TSN control plane around **encrypted settlement tokens**, **One-Time Decryption Tokens (OTDT)**, **claim points**, **claim leases**, a **commitment registry**, and **smart recovery**. The goal is to make each TSN transfer auditable and recoverable without storing sender wallets, recipient wallets, token balances, phone numbers, or decrypted settlement data in the public registry. + +At a high level: + +```text +TINS identity resolution + -> TSN payment intent + -> encrypted settlement token + commitment registry entry + -> Cranker intent work + -> claim points + -> claim lease + OTDT + -> settlement-token decryption in operator memory + -> recipient payout/proof + -> recoverable commitment registry state + -> recovery queue + -> smart recovery back to liquidity +``` + +--- + +## What changed in this update + +| Area | New or modified behavior | Primary files | +| --- | --- | --- | +| Settlement-token primitives | Adds encrypted settlement payload creation, commitment hashing, Cranker DNA authorization, authenticated decryption, and OTDT hash generation. | `tsn-sdk/src/settlement-token.ts` | +| TSN mempool runtime | Adds commitment registry, claim-point ledger, claim leases, recovery queue, liquidity metrics, proof acceptance, and recovery completion. | `tsn-sdk/src/mempool.ts` | +| TSN HTTP operations | Adds endpoints for intent verification, claim leases, registry inspection, recovery queue inspection, liquidity metrics, and recovery completion. | `tsn-sdk/src/server.ts` | +| SDK contracts | Adds explicit types for registry entries, claim points, claim leases, recovery jobs, liquidity metrics, and proof metadata. | `tsn-sdk/src/contracts.ts` | +| Python Cranker daemon | Adds an autonomous Python scheduler for intent work, settlement work, OTDT-gated decryption, recovery enqueueing, and priority recovery. | `tsn-cranker-op-daemon/scripts/cranker_daemon.py` | +| Operator configuration | Adds settlement-token secret, Cranker DNA, low-liquidity threshold, and recovery reward settings. | `backend/.env.example`, `tsn-cranker-op-daemon/.env.example` | + +--- + +## Concept map for lay readers + +A normal payment app often stores or exposes a direct path such as: + +```text +sender wallet -> recipient wallet +``` + +TrustLink Pay avoids making that direct wallet path the normal product surface. A sender pays a **TIN**, TSN records a **payment intent**, and Crankers complete settlement work through a proof-and-recovery workflow. + +The encrypted settlement token can be understood as a sealed instruction envelope. The commitment registry stores the envelope and its hash, but not the private contents. A Cranker must first earn the right to work on claims, then receive a one-time decryption authorization before the envelope can be opened for settlement execution. + +This gives the system three important properties: + +1. **Privacy boundary:** the registry does not store sender wallets, recipient wallets, balances, phone numbers, or decrypted settlement tokens. +2. **Replay resistance:** claim leases and OTDT hashes prevent duplicate settlement attempts for the same transfer. +3. **Recovery accounting:** once settlement proof is accepted, recoverable state is derived from the commitment registry rather than a separate claimed flag. + +--- + +## Components and responsibilities + +### TINS + +TINS remains the payment identity layer. Users share a 10-digit TIN instead of a wallet address. TSN receives a resolved route or recipient hash from the application/backend, but the commitment registry does not store the recipient's main wallet. + +### SAS + +SAS remains the verification layer. SAS attestations should be validated when recipient trust needs to be displayed or policy decisions need verified credentials. SAS must not place sensitive personal data in TSN registry records. Verification outputs should be reduced to trust decisions or attestation references. + +### TSN + +TSN owns settlement execution state: + +- payment intents +- encrypted settlement-token commitments +- intent work +- claim points +- claim leases +- settlement proofs +- recoverable registry state +- recovery queue and recovery proofs + +### Crankers + +Crankers perform operational work: + +- monitor TSN queues +- verify intent structure and signatures +- earn claim points through intent work +- acquire claim leases +- receive an OTDT hash before decryption +- decrypt settlement tokens only in memory +- perform settlement and proof submission +- complete recovery jobs when liquidity needs replenishment + +--- + +## Settlement-token primitive + +### Plaintext contents + +The settlement-token plaintext is the private instruction payload that Crankers need for settlement execution. In the SDK implementation it contains: + +```ts +{ + transferId: string; + recipientHash: string; + tokenMintAddress: string; + amount: number; + epoch: number; + nonce: string; + issuedAt: string; +} +``` + +This plaintext is **not** stored in the commitment registry. Its hash becomes the registry commitment. + +### Encrypted envelope + +The encrypted envelope records only data needed to verify and decrypt the token later: + +```ts +{ + version: 1, + algorithm: "TSN-HKDF-SHA256-STREAM-HMAC", + salt: string, + nonce: string, + aad: string, + aadHash: string, + authorizedCrankerDnaHash: string, + ciphertext: string, + tag: string +} +``` + +Implementation note: the current TypeScript implementation uses Node.js cryptographic primitives and a deterministic HMAC-derived stream construction with an authentication tag. Production deployments should treat `TSN_SETTLEMENT_TOKEN_MASTER_KEY` as a high-value secret and rotate it through deployment secrets rather than committing it to the repository. + +### Cranker DNA authorization + +Cranker DNA is an operator authorization secret or policy input. The public envelope stores only a DNA hash: + +```text +sha256("tsn-cranker-dna:" + TSN_CRANKER_DNA) +``` + +A Cranker whose DNA does not match the envelope cannot decrypt the token. + +--- + +## Commitment registry + +The commitment registry is the single source of truth for settlement verification and recoverability. + +### Stored fields + +A registry entry stores: + +| Field | Purpose | +| --- | --- | +| `transferId` | Stable transfer identifier. | +| `encryptedSettlementToken` | Public encrypted settlement envelope. | +| `commitmentHash` | Hash of the decrypted settlement token. | +| `timestamp` | Time the commitment entered the registry. | +| `epoch` | TSN epoch used for accounting and recovery. | +| `recoverable` | Whether proof has been accepted and recovery is now permitted. | +| `intentVerifierPubkey` | Optional Cranker that completed intent verification. | +| `settlementCommitmentHash` | Hash committed after settlement proof. | +| `settlementProofTx` | Proof transaction or proof reference. | +| `otdtHash` | Hash of the one-time decryption token issued for the claim lease. | +| `recoveryProofTx` | Proof reference for recovery execution. | +| `updatedAt` | Last runtime mutation timestamp. | + +### Explicitly forbidden fields + +The registry must never store: + +- sender wallet address +- recipient main wallet address +- decrypted settlement token +- token balance data +- phone number +- WhatsApp identifier +- SAS personally identifiable information + +The current implementation keeps settlement destination details in claim requests and operator-local processing, not in commitment registry entries. + +--- + +## End-to-end TSN flow + +### 1. Identity and verification + +1. Sender enters a recipient TIN. +2. TINS resolves the payment identity and route metadata. +3. SAS verification may be checked for recipient confidence. +4. WhatsApp may be used only as the social confidence/notification channel. + +### 2. Payment intent creation + +1. Backend or SDK creates a TSN payment intent. +2. The TSN SDK generates an encrypted settlement token when one is not supplied. +3. The intent is written to the mempool. +4. The commitment registry receives the encrypted token and commitment hash in the same mempool transition. + +Example TypeScript flow: + +```ts +import { JsonFileTsnMempool } from "@trustlink/tsn-sdk/mempool"; +import { buildCreateIntentRequest } from "@trustlink/tsn-sdk/contracts"; + +const tsn = new JsonFileTsnMempool(); + +await tsn.postIntent( + buildCreateIntentRequest({ + paymentId: transferId, + recipientHash, + tokenMintAddress, + amount, + source: "trustlink-pay", + }), +); +``` + +### 3. Intent work + +Crankers perform intent work before settlement work. Intent work validates: + +- required TSN payment intent fields +- sender authorization and expiry when supplied +- encrypted settlement token presence +- commitment hash presence +- epoch/routing consistency +- registry entry existence + +Successful intent work moves the intent to `escrowed` and credits the Cranker with claim points. + +### 4. Claim points + +Claim points represent useful work already performed by a Cranker. They prevent operators from skipping verification and going directly to settlement work. + +```text +1 verified intent work item -> +1 claim point +1 claim lease acquisition -> -1 available claim point +``` + +### 5. Claim lease and OTDT + +A Cranker must acquire a claim lease before settlement. Lease acquisition: + +1. checks that the transfer is not already recoverable; +2. checks that no OTDT hash already exists; +3. checks that the Cranker has available claim points; +4. spends a claim point; +5. creates an active lease; +6. generates an OTDT hash; +7. writes the OTDT hash to the registry. + +The OTDT hash is written before decryption so duplicate settlement attempts fail against registry state. + +### 6. Settlement work + +Settlement work uses the lease and OTDT state: + +```text +active claim lease + -> OTDT hash exists in registry + -> settlement token decrypted in Cranker memory + -> recipient payout / settlement action + -> settlement commitment hash generated + -> proof submitted + -> registry marked recoverable +``` + +The decrypted token is not written back to the registry. + +### 7. Recovery queue + +After settlement proof is accepted, recoverable state is derived from the commitment registry. TSN creates a recovery job with: + +- transfer ID +- epoch +- recoverable amount +- vault source +- recovery reward +- priority score +- job status + +### 8. Smart recovery + +Smart recovery monitors: + +- active liquidity +- pending intent amount +- vault balance +- settlement velocity +- liquidity consumption rate +- configured low-liquidity threshold + +Priority increases when liquidity is below threshold or demand is high. Recovery jobs are open work: the first eligible Cranker that completes the job records recovery proof and returns funds to liquidity. + +--- + +## Mempool runtime operations + +The TypeScript mempool runtime now exposes these operations: + +| Operation | Purpose | +| --- | --- | +| `postIntent` | Creates a TSN intent and registry commitment. | +| `postClaimRequest` | Adds recipient claim/settlement work. | +| `listPendingIntentWork` | Returns intent work for Crankers. | +| `submitIntentVerification` | Marks intent work verified and awards claim points. | +| `acquireClaimLease` | Spends claim points and issues OTDT hash. | +| `postProof` | Accepts settlement proof and marks transfer recoverable. | +| `listCommitmentRegistry` | Shows public registry state. | +| `listClaimPointLedger` | Shows Cranker claim-point accounting. | +| `listClaimLeases` | Shows active/completed leases. | +| `listRecoveryQueue` | Shows recovery jobs. | +| `getLiquidityMetrics` | Shows liquidity and demand metrics. | +| `completeRecoveryJob` | Completes recovery and returns amount to liquidity metrics. | + +HTTP server routes mirror those operations: + +```text +POST /intents +GET /intents +POST /claim-requests +GET /claim-requests +GET /intent-work +GET /work +GET /commitment-registry +GET /claim-points +GET /claim-leases +GET /recovery-queue +GET /liquidity-metrics +POST /intents/:id/verify +POST /intents/:id/claim-lease +PATCH /intents/:id/status +PATCH /claim-requests/:id/status +POST /proofs +POST /recovery-queue/:id/complete +``` + +--- + +## Python Cranker daemon + +The Python daemon is designed for operator automation and local runtime validation. It continuously processes queues from the TSN JSON mempool file. + +### Responsibilities + +1. Load TSN mempool state. +2. Verify pending intents. +3. Award claim points. +4. Acquire claim leases. +5. Generate OTDT hashes. +6. Decrypt settlement tokens in memory. +7. Generate settlement commitment hashes. +8. Mark registry entries recoverable. +9. Enqueue recovery jobs. +10. Complete priority recovery jobs. +11. Update liquidity metrics. + +### One-shot execution + +```bash +TSN_SETTLEMENT_TOKEN_MASTER_KEY=<32-byte-base64-or-64-char-hex-secret> \ +TSN_CRANKER_ONCE=true \ +python tsn-cranker-op-daemon/scripts/cranker_daemon.py +``` + +### Continuous execution + +```bash +TSN_SETTLEMENT_TOKEN_MASTER_KEY=<32-byte-base64-or-64-char-hex-secret> \ +npm --prefix tsn-cranker-op-daemon run crank:python +``` + +### Important environment variables + +| Variable | Purpose | +| --- | --- | +| `TSN_SETTLEMENT_TOKEN_MASTER_KEY` | Secret used to derive settlement-token encryption/decryption keys. | +| `TSN_CRANKER_DNA` | Cranker DNA authorization input; only its hash is used in envelopes. | +| `TSN_MEMPOOL_FILE` | Local JSON mempool path for the Python scheduler. | +| `TSN_CRANKER_POLL_SECONDS` | Continuous scheduler loop interval. | +| `TSN_LOW_LIQUIDITY_THRESHOLD` | Liquidity threshold used for recovery priority. | +| `TSN_RECOVERY_REWARD_BPS` | Recovery reward basis points used by the TypeScript mempool runtime. | + +--- + +## Security and privacy considerations + +### What the update protects + +- **Recipient-wallet privacy:** the commitment registry does not store recipient main wallets. +- **Sender-wallet privacy:** registry commitments do not require sender wallet storage. +- **Token secrecy:** decrypted settlement-token contents remain in Cranker memory. +- **Duplicate-claim resistance:** OTDT hash presence blocks repeated claim-lease/decryption attempts. +- **Operator gating:** Crankers need claim points before claim leases. +- **Recovery derivation:** recoverability is derived from accepted settlement proof in the registry. + +### What this update does not claim + +- It does not make Solana transactions invisible. +- It does not implement live confidential transfer swaps or TF-token swaps. +- It does not place SAS PII on-chain. +- It does not use WhatsApp as a protocol identity; WhatsApp remains a social confidence and notification channel only. + +### Operational rules + +- Keep `TSN_SETTLEMENT_TOKEN_MASTER_KEY` in secure deployment secrets. +- Rotate Cranker DNA and settlement-token master keys through controlled operations. +- Treat registry entries as public metadata. +- Do not add sender/recipient wallet fields to registry entries. +- Validate SAS attestations at the trust layer without writing sensitive attestation payloads into TSN settlement state. + +--- + +## Testing notes + +Recommended checks after changes to this area: + +```bash +TSN_SETTLEMENT_TOKEN_MASTER_KEY= npm --prefix tsn-sdk run build +npm --prefix backend run typecheck +python -m py_compile tsn-cranker-op-daemon/scripts/cranker_daemon.py +``` + +Recommended runtime smoke path: + +1. Create a TSN intent through `JsonFileTsnMempool.postIntent`. +2. Confirm a commitment registry entry exists. +3. Post a claim request. +4. Run intent verification. +5. Acquire a claim lease. +6. Confirm OTDT hash exists in the registry. +7. Submit settlement proof. +8. Confirm registry entry is recoverable. +9. Confirm recovery queue contains a job. +10. Run Python Cranker one-shot. +11. Confirm recovery job completes and liquidity metrics update. + +--- + +## Implementation notes and future hardening + +- The current runtime is a local/JSON and HTTP control-plane implementation intended to make TSN behavior executable end-to-end in development and operator environments. +- On-chain program changes should preserve the same privacy boundary: no sender wallet, recipient wallet, decrypted token, phone number, balance, or SAS PII in the commitment registry. +- Production deployments should add durable storage, access control, structured audit logs, and key-rotation ceremonies around settlement-token master keys and Cranker DNA. +- Additional tests should cover duplicate OTDT issuance, mismatched DNA, expired leases, duplicate proof submission, low-liquidity priority scoring, and recovery competition. diff --git a/docs/README.md b/docs/README.md index 7525b6aa..30322deb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -31,6 +31,7 @@ Phone numbers, WhatsApp, and social identities are optional application-layer li | [CRANKER.md](./CRANKER.md) | Cranker operator guide | | [LIQUIDITY.md](./LIQUIDITY.md) | Vault liquidity and LP model | | [EPOCH-SETTLEMENT.md](./EPOCH-SETTLEMENT.md) | Epoch reimbursement and accounting | +| [OTDT-SMART-RECOVERY.md](./OTDT-SMART-RECOVERY.md) | OTDT, claim leases, commitment registry, and smart recovery runtime | --- diff --git a/frontend/src/components/experiences/send-experience.tsx b/frontend/src/components/experiences/send-experience.tsx index d23300a2..161309b2 100644 --- a/frontend/src/components/experiences/send-experience.tsx +++ b/frontend/src/components/experiences/send-experience.tsx @@ -1,18 +1,15 @@ "use client"; import Link from "next/link"; -import { FormEvent, useEffect, useMemo, useRef, useState, type Dispatch, type SetStateAction } from "react"; +import { FormEvent, useEffect, useMemo, useRef, useState } from "react"; import { useSearchParams } from "next/navigation"; import { AppMobileShell } from "@/src/components/layout/app-mobile-shell"; -import { PaymentNotificationReceipt } from "@/src/components/payment-notification-receipt"; import { PinGateModal } from "@/src/components/modals/pin-gate-modal"; import { PhoneNumberInput } from "@/src/components/phone-number-input"; import { SectionLoader } from "@/src/components/section-loader"; -import { SuccessIcon } from "@/src/components/success-icon"; import { useToast } from "@/src/components/toast-provider"; import { WalletPickerModal } from "@/src/components/modals/wallet-picker-modal"; -import { shortenAddress } from "@/src/lib/address"; import { apiGet, apiPost } from "@/src/lib/api"; import { isPaymentNotificationFinal } from "@/src/lib/formatters"; import { buildPhoneResolutionPlan } from "@/src/lib/phone-input-resolution"; @@ -24,9 +21,7 @@ import { type CountryOption, } from "@/src/lib/phone-countries"; import { loadPreferredCountryIso2, rememberCountryUsage } from "@/src/lib/phone-preferences"; -import { shareInviteMessage } from "@/src/lib/share"; import type { - PaymentNotificationStatus, PaymentRecord, RecipientLookupResult, WalletTokenOption, @@ -55,82 +50,16 @@ import { type SendCostEstimate, } from "@/src/components/experiences/send/shared/send-cost"; import { getSendGuidance } from "@/src/components/experiences/send/shared/send-guidance"; -import { AlertCircle, ChevronDown, ChevronRight, Globe, Loader2, RefreshCw, Search, X } from "lucide-react"; +import { AlertCircle, ChevronDown, ChevronRight, Globe, Loader2, RefreshCw } from "lucide-react"; +import { CountrySearchModal } from "@/src/components/experiences/send/components/CountrySearchModal"; +import { SendSuccessPanel } from "@/src/components/experiences/send/components/SendSuccessPanel"; +import { formatTokenBalance } from "@/src/components/experiences/send/utils/formatting"; +import { looksLikeTinCandidate, normalizeTinInput } from "@/src/components/experiences/send/utils/tin-input"; +import { resetRecipientResolution } from "@/src/components/experiences/send/utils/reset-recipient-resolution"; +import type { PhoneVerificationDetails, RecipientVerificationState, ResolvedRecipientLookup, SendSuccessState } from "@/src/components/experiences/send/types"; const SEND_RECEIPT_REFRESH_INTERVAL_MS = 20_000; -function paymentStatusLabel(status: PaymentRecord["status"]) { - if (status === "created") return "processing"; - return status.replace(/_/g, " "); -} - -function formatTokenBalance(balance: number, symbol: string) { - const digits = symbol === "SOL" ? 4 : 2; - return new Intl.NumberFormat("en-US", { minimumFractionDigits: 0, maximumFractionDigits: digits }).format(balance); -} - -function formatReceiptTime(value: string | null) { - if (!value) return null; - return new Intl.DateTimeFormat("en", { month: "short", day: "numeric", hour: "numeric", minute: "2-digit" }).format(new Date(value)); -} - -type ResolvedRecipientLookup = { verification: WhatsAppNumberVerificationResult; recipient: RecipientLookupResult | null; normalizedPhone: string; country: CountryOption | null }; - -function normalizeTinInput(value: string) { - const trimmed = value.trim(); - const digits = trimmed.replace(/\D/g, ""); - const hasTinPrefix = /^tin[:\s_-]*/i.test(trimmed); - if (!/^\d{10}$/.test(digits)) return null; - if (!hasTinPrefix && trimmed.startsWith("+")) return null; - - const checkDigit = Number(digits[9]); - let sum = 0; - let double = true; - for (const char of digits.slice(0, 9).split("").reverse()) { - let digit = Number(char); - if (double) { - digit *= 2; - if (digit > 9) digit -= 9; - } - sum += digit; - double = !double; - } - return ((10 - (sum % 10)) % 10) === checkDigit ? digits : null; -} - -function looksLikeTinCandidate(value: string) { - const trimmed = value.trim(); - return /^tin[:\s_-]*/i.test(trimmed) || (/^\d{10}$/.test(trimmed.replace(/\D/g, "")) && !trimmed.startsWith("+")); -} - -function resetRecipientResolution(params: { - setPhoneVerificationState: (value: "idle" | "checking" | "valid" | "warning" | "invalid") => void; - setPhoneVerificationLabel: (value: string | null) => void; - setPhoneVerificationDetails: (value: { displayName: string | null; profilePic: string | null; exists: boolean; isBusiness: boolean; url: string; resolvedPhoneNumber?: string | null; detectedCountry?: CountryOption | null } | null) => void; - setReceiverWhatsAppVerified: (value: boolean) => void; - setReceiverCheckSkipped: (value: boolean) => void; - setRecipientPreview: (value: RecipientLookupResult | null) => void; - setLookupError: (value: string | null) => void; - setPreviewBusy: (value: boolean) => void; - setShowCountryFallback: (value: boolean) => void; - setSuggestedCountries: (value: CountryOption[]) => void; - setReceiverCountry: (value: CountryOption | null) => void; - setForm: Dispatch>; -}) { - params.setPhoneVerificationState("idle"); - params.setPhoneVerificationLabel(null); - params.setPhoneVerificationDetails(null); - params.setReceiverWhatsAppVerified(false); - params.setReceiverCheckSkipped(false); - params.setRecipientPreview(null); - params.setLookupError(null); - params.setPreviewBusy(false); - params.setShowCountryFallback(false); - params.setSuggestedCountries([]); - params.setReceiverCountry(null); - params.setForm((current) => ({ ...current, receiverPhone: "" })); -} - export function SendExperience() { const { hydrated, accessToken, user, pendingAuth, completePendingAuth, logout } = useAuthenticatedSession("/app/send"); const searchParams = useSearchParams(); @@ -149,9 +78,9 @@ export function SendExperience() { const [error, setError] = useState(null); const [lookupError, setLookupError] = useState(null); const [recipientPreview, setRecipientPreview] = useState(null); - const [phoneVerificationState, setPhoneVerificationState] = useState<"idle" | "checking" | "valid" | "warning" | "invalid">("idle"); + const [phoneVerificationState, setPhoneVerificationState] = useState("idle"); const [phoneVerificationLabel, setPhoneVerificationLabel] = useState(null); - const [phoneVerificationDetails, setPhoneVerificationDetails] = useState<{ displayName: string | null; profilePic: string | null; exists: boolean; isBusiness: boolean; url: string; resolvedPhoneNumber?: string | null; detectedCountry?: CountryOption | null } | null>(null); + const [phoneVerificationDetails, setPhoneVerificationDetails] = useState(null); const [receiverWhatsAppVerified, setReceiverWhatsAppVerified] = useState(false); const [receiverCheckSkipped, setReceiverCheckSkipped] = useState(false); const [supportedTokens, setSupportedTokens] = useState([]); @@ -163,16 +92,7 @@ export function SendExperience() { const [tokenPickerOpen, setTokenPickerOpen] = useState(false); const [sendCostEstimate, setSendCostEstimate] = useState(null); const [estimateError, setEstimateError] = useState(null); - const [sendSuccess, setSendSuccess] = useState<{ - paymentId: string; status: PaymentRecord["status"]; notificationStatus: PaymentNotificationStatus; - notificationSentAt: string | null; notificationDeliveredAt: string | null; notificationReadAt: string | null; - notificationFailedAt: string | null; referenceCode: string; senderDisplayName: string; senderHandle: string; - escrowAccount: string | null; blockchainSignature: string; blockchainMode: "tsn" | "mock" | "devnet"; - depositAddress: string | null; notificationRetrying: boolean; notificationAttemptCount: number; - manualInviteRequired: boolean; - inviteShare: { onboardingLink: string; inviteMessage: string } | null; - receiverPhone: string; recipientName: string; amount: string; token: string; - } | null>(null); + const [sendSuccess, setSendSuccess] = useState(null); const [shareBusy, setShareBusy] = useState(false); const [countrySearchOpen, setCountrySearchOpen] = useState(false); const [countrySearchQuery, setCountrySearchQuery] = useState(""); @@ -527,101 +447,15 @@ export function SendExperience() { {/* ═══════════ SEND SUCCESS ═══════════ */} {sendSuccess ? ( -
-
- -
Awaiting Cranker Verification
-

- {sendSuccess.amount} {sendSuccess.token} -

-

- {sendSuccess.manualInviteRequired - ? `Authorization queued for ${sendSuccess.recipientName}. Share the invite manually.` - : sendSuccess.notificationRetrying - ? `Authorization queued for ${sendSuccess.recipientName}. WhatsApp delivery retrying.` - : `Intent queued for ${sendSuccess.recipientName}. Waiting for Cranker verification and on-chain submission.`} -

-
- -
- {/* Left: Transfer details */} -
- {[ - { label: "Recipient", value: sendSuccess.recipientName }, - { label: "WhatsApp", value: sendSuccess.receiverPhone }, - { label: "Reference", value: sendSuccess.referenceCode }, - { label: "Status", value: paymentStatusLabel(sendSuccess.status), capitalize: true }, - ].map((row) => ( -
- {row.label} - {row.value} -
- ))} -
- - {/* Right: Delivery details */} -
- {!sendSuccess.manualInviteRequired ? ( -
- WhatsApp receipt - -
- ) : ( -
- Sender invite - Share manually -
- )} - - {sendSuccess.notificationRetrying ? ( -
- Delivery retries - {sendSuccess.notificationAttemptCount} -
- ) : null} - - {!sendSuccess.manualInviteRequired && receiptTimestamp ? ( -
- Receipt updated - {formatReceiptTime(receiptTimestamp)} -
- ) : null} - -
- {sendSuccess.blockchainMode === "mock" ? "Mock ref" : sendSuccess.blockchainMode === "tsn" ? "Intent id" : "Deposit tx"} - {shortenAddress(sendSuccess.blockchainSignature)} -
-
-
- -
- {sendSuccess.blockchainMode === "mock" - ? "Mock mode \u2014 reference is not an on-chain signature." - : sendSuccess.blockchainMode === "tsn" - ? "Authorization is in the TSN mempool. Your wallet did not broadcast a Solana transaction; a Cranker verifies and submits settlement." - : "Receipts refresh while delivery is unresolved."} -
- - {sendSuccess.manualInviteRequired && sendSuccess.inviteShare ? ( -
-
Shareable invite
-
{sendSuccess.inviteShare.inviteMessage}
- -
- ) : null} - -
- Back home - -
-
+ { setSendSuccess(null); setNotice(null); }} + /> ) : ( /* ═══════════ SEND FORM ═══════════ */ @@ -966,68 +800,25 @@ export function SendExperience() { ) : null} - {/* ═══════════ COUNTRY SEARCH MODAL ═══════════ */} - {countrySearchOpen ? ( -
setCountrySearchOpen(false)}> -
e.stopPropagation()}> - {/* Header + search */} -
-
-
-

Select Country

-

Choose the recipient country code

-
- -
-
- - setCountrySearchQuery(e.target.value)} - placeholder="Search by name or code..." - autoFocus - className="w-full rounded-[14px] border border-[var(--field-border)] bg-[var(--field)] py-2.5 pl-10 pr-4 text-[var(--text)] outline-none placeholder:text-[var(--text-faint)] focus:border-[var(--accent-border)]" - /> -
-
- - {/* Country list */} -
-
- {filteredCountries.map((c) => { - const isActive = c.iso2 === displayCountry?.iso2; - return ( - - ); - })} - {filteredCountries.length === 0 ? ( -
No countries match {countrySearchQuery}
- ) : null} -
-
-
-
- ) : null} + setCountrySearchOpen(false)} + onSelectCountry={(country) => { + setManualCountry(country); + setManualCountryLocked(true); + setReceiverCountry(country); + setReceiverCheckSkipped(false); + setLookupError(null); + setShowCountryFallback(false); + setPhoneVerificationState("checking"); + setPhoneVerificationLabel(`Retrying with ${country.name}...`); + setCountrySearchOpen(false); + }} + /> { if (!connectingWalletId) setWalletPickerOpen(false); }} onSelect={(id) => void handleWalletSelect(id)} /> diff --git a/frontend/src/components/experiences/send/components/CountrySearchModal.tsx b/frontend/src/components/experiences/send/components/CountrySearchModal.tsx new file mode 100644 index 00000000..0979e095 --- /dev/null +++ b/frontend/src/components/experiences/send/components/CountrySearchModal.tsx @@ -0,0 +1,84 @@ +"use client"; + +import { Search, X } from "lucide-react"; +import type { CountryOption } from "@/src/lib/phone-countries"; + +type CountrySearchModalProps = { + open: boolean; + query: string; + countries: CountryOption[]; + activeCountry: CountryOption | null; + onQueryChange: (value: string) => void; + onClose: () => void; + onSelectCountry: (country: CountryOption) => void; +}; + +export function CountrySearchModal({ + open, + query, + countries, + activeCountry, + onQueryChange, + onClose, + onSelectCountry, +}: CountrySearchModalProps) { + if (!open) return null; + + return ( +
+
event.stopPropagation()}> +
+
+
+

Select Country

+

Choose the recipient country code

+
+ +
+
+ + onQueryChange(event.target.value)} + placeholder="Search by name or code..." + autoFocus + className="w-full rounded-[14px] border border-[var(--field-border)] bg-[var(--field)] py-2.5 pl-10 pr-4 text-[var(--text)] outline-none placeholder:text-[var(--text-faint)] focus:border-[var(--accent-border)]" + /> +
+
+ +
+
+ {countries.map((country) => { + const isActive = country.iso2 === activeCountry?.iso2; + return ( + + ); + })} + {countries.length === 0 ? ( +
No countries match {query}
+ ) : null} +
+
+
+
+ ); +} diff --git a/frontend/src/components/experiences/send/components/SendSuccessPanel.tsx b/frontend/src/components/experiences/send/components/SendSuccessPanel.tsx new file mode 100644 index 00000000..7e63cbad --- /dev/null +++ b/frontend/src/components/experiences/send/components/SendSuccessPanel.tsx @@ -0,0 +1,137 @@ +"use client"; + +import Link from "next/link"; +import { PaymentNotificationReceipt } from "@/src/components/payment-notification-receipt"; +import { SuccessIcon } from "@/src/components/success-icon"; +import { shortenAddress } from "@/src/lib/address"; +import { shareInviteMessage } from "@/src/lib/share"; +import { formatReceiptTime, paymentStatusLabel } from "@/src/components/experiences/send/utils/formatting"; +import type { SendSuccessState } from "@/src/components/experiences/send/types"; + +type SendSuccessPanelProps = { + sendSuccess: SendSuccessState; + receiptTimestamp: string | null; + shareBusy: boolean; + onShareBusyChange: (value: boolean) => void; + onError: (message: string) => void; + onToast: (message: string) => void; + onSendAnother: () => void; +}; + +export function SendSuccessPanel({ + sendSuccess, + receiptTimestamp, + shareBusy, + onShareBusyChange, + onError, + onToast, + onSendAnother, +}: SendSuccessPanelProps) { + async function handleShareInvite() { + onShareBusyChange(true); + try { + const outcome = await shareInviteMessage(sendSuccess.inviteShare!.inviteMessage); + onToast(outcome === "shared" ? "Share dialog opened." : "Invite copied."); + } catch (error) { + onError(error instanceof Error ? error.message : "Could not share invite"); + } finally { + onShareBusyChange(false); + } + } + + return ( +
+
+ +
Awaiting Cranker Verification
+

+ {sendSuccess.amount} {sendSuccess.token} +

+

+ {sendSuccess.manualInviteRequired + ? `Authorization queued for ${sendSuccess.recipientName}. Share the invite manually.` + : sendSuccess.notificationRetrying + ? `Authorization queued for ${sendSuccess.recipientName}. WhatsApp delivery retrying.` + : `Intent queued for ${sendSuccess.recipientName}. Waiting for Cranker verification and on-chain submission.`} +

+
+ +
+
+ {[ + { label: "Recipient", value: sendSuccess.recipientName }, + { label: "WhatsApp", value: sendSuccess.receiverPhone }, + { label: "Reference", value: sendSuccess.referenceCode }, + { label: "Status", value: paymentStatusLabel(sendSuccess.status), capitalize: true }, + ].map((row) => ( +
+ {row.label} + {row.value} +
+ ))} +
+ +
+ {!sendSuccess.manualInviteRequired ? ( +
+ WhatsApp receipt + +
+ ) : ( +
+ Sender invite + Share manually +
+ )} + + {sendSuccess.notificationRetrying ? ( +
+ Delivery retries + {sendSuccess.notificationAttemptCount} +
+ ) : null} + + {!sendSuccess.manualInviteRequired && receiptTimestamp ? ( +
+ Receipt updated + {formatReceiptTime(receiptTimestamp)} +
+ ) : null} + +
+ {sendSuccess.blockchainMode === "mock" ? "Mock ref" : sendSuccess.blockchainMode === "tsn" ? "Intent id" : "Deposit tx"} + {shortenAddress(sendSuccess.blockchainSignature)} +
+
+
+ +
+ {sendSuccess.blockchainMode === "mock" + ? "Mock mode — reference is not an on-chain signature." + : sendSuccess.blockchainMode === "tsn" + ? "Authorization is in the TSN mempool. Your wallet did not broadcast a Solana transaction; a Cranker verifies and submits settlement." + : "Receipts refresh while delivery is unresolved."} +
+ + {sendSuccess.manualInviteRequired && sendSuccess.inviteShare ? ( +
+
Shareable invite
+
{sendSuccess.inviteShare.inviteMessage}
+ +
+ ) : null} + +
+ Back home + +
+
+ ); +} diff --git a/frontend/src/components/experiences/send/types.ts b/frontend/src/components/experiences/send/types.ts new file mode 100644 index 00000000..8b27a240 --- /dev/null +++ b/frontend/src/components/experiences/send/types.ts @@ -0,0 +1,52 @@ +import type { PaymentNotificationStatus, PaymentRecord, RecipientLookupResult, WhatsAppNumberVerificationResult } from "@/src/lib/types"; +import type { CountryOption } from "@/src/lib/phone-countries"; + +export type SendFormState = { + receiverPhone: string; + amount: string; + token: string; +}; + +export type SendSuccessState = { + paymentId: string; + status: PaymentRecord["status"]; + notificationStatus: PaymentNotificationStatus; + notificationSentAt: string | null; + notificationDeliveredAt: string | null; + notificationReadAt: string | null; + notificationFailedAt: string | null; + referenceCode: string; + senderDisplayName: string; + senderHandle: string; + escrowAccount: string | null; + blockchainSignature: string; + blockchainMode: "tsn" | "mock" | "devnet"; + depositAddress: string | null; + notificationRetrying: boolean; + notificationAttemptCount: number; + manualInviteRequired: boolean; + inviteShare: { onboardingLink: string; inviteMessage: string } | null; + receiverPhone: string; + recipientName: string; + amount: string; + token: string; +}; + +export type PhoneVerificationDetails = { + displayName: string | null; + profilePic: string | null; + exists: boolean; + isBusiness: boolean; + url: string; + resolvedPhoneNumber?: string | null; + detectedCountry?: CountryOption | null; +}; + +export type RecipientVerificationState = "idle" | "checking" | "valid" | "warning" | "invalid"; + +export type ResolvedRecipientLookup = { + verification: WhatsAppNumberVerificationResult; + recipient: RecipientLookupResult | null; + normalizedPhone: string; + country: CountryOption | null; +}; diff --git a/frontend/src/components/experiences/send/utils/formatting.ts b/frontend/src/components/experiences/send/utils/formatting.ts new file mode 100644 index 00000000..9da17b3b --- /dev/null +++ b/frontend/src/components/experiences/send/utils/formatting.ts @@ -0,0 +1,16 @@ +import type { PaymentRecord } from "@/src/lib/types"; + +export function paymentStatusLabel(status: PaymentRecord["status"]) { + if (status === "created") return "processing"; + return status.replace(/_/g, " "); +} + +export function formatTokenBalance(balance: number, symbol: string) { + const digits = symbol === "SOL" ? 4 : 2; + return new Intl.NumberFormat("en-US", { minimumFractionDigits: 0, maximumFractionDigits: digits }).format(balance); +} + +export function formatReceiptTime(value: string | null) { + if (!value) return null; + return new Intl.DateTimeFormat("en", { month: "short", day: "numeric", hour: "numeric", minute: "2-digit" }).format(new Date(value)); +} diff --git a/frontend/src/components/experiences/send/utils/reset-recipient-resolution.ts b/frontend/src/components/experiences/send/utils/reset-recipient-resolution.ts new file mode 100644 index 00000000..25e68119 --- /dev/null +++ b/frontend/src/components/experiences/send/utils/reset-recipient-resolution.ts @@ -0,0 +1,32 @@ +import type { Dispatch, SetStateAction } from "react"; +import type { RecipientLookupResult } from "@/src/lib/types"; +import type { CountryOption } from "@/src/lib/phone-countries"; +import type { PhoneVerificationDetails, RecipientVerificationState, SendFormState } from "@/src/components/experiences/send/types"; + +export function resetRecipientResolution(params: { + setPhoneVerificationState: (value: RecipientVerificationState) => void; + setPhoneVerificationLabel: (value: string | null) => void; + setPhoneVerificationDetails: (value: PhoneVerificationDetails | null) => void; + setReceiverWhatsAppVerified: (value: boolean) => void; + setReceiverCheckSkipped: (value: boolean) => void; + setRecipientPreview: (value: RecipientLookupResult | null) => void; + setLookupError: (value: string | null) => void; + setPreviewBusy: (value: boolean) => void; + setShowCountryFallback: (value: boolean) => void; + setSuggestedCountries: (value: CountryOption[]) => void; + setReceiverCountry: (value: CountryOption | null) => void; + setForm: Dispatch>; +}) { + params.setPhoneVerificationState("idle"); + params.setPhoneVerificationLabel(null); + params.setPhoneVerificationDetails(null); + params.setReceiverWhatsAppVerified(false); + params.setReceiverCheckSkipped(false); + params.setRecipientPreview(null); + params.setLookupError(null); + params.setPreviewBusy(false); + params.setShowCountryFallback(false); + params.setSuggestedCountries([]); + params.setReceiverCountry(null); + params.setForm((current) => ({ ...current, receiverPhone: "" })); +} diff --git a/frontend/src/components/experiences/send/utils/tin-input.ts b/frontend/src/components/experiences/send/utils/tin-input.ts new file mode 100644 index 00000000..24d9ab0e --- /dev/null +++ b/frontend/src/components/experiences/send/utils/tin-input.ts @@ -0,0 +1,26 @@ +export function normalizeTinInput(value: string) { + const trimmed = value.trim(); + const digits = trimmed.replace(/\D/g, ""); + const hasTinPrefix = /^tin[:\s_-]*/i.test(trimmed); + if (!/^\d{10}$/.test(digits)) return null; + if (!hasTinPrefix && trimmed.startsWith("+")) return null; + + const checkDigit = Number(digits[9]); + let sum = 0; + let double = true; + for (const char of digits.slice(0, 9).split("").reverse()) { + let digit = Number(char); + if (double) { + digit *= 2; + if (digit > 9) digit -= 9; + } + sum += digit; + double = !double; + } + return ((10 - (sum % 10)) % 10) === checkDigit ? digits : null; +} + +export function looksLikeTinCandidate(value: string) { + const trimmed = value.trim(); + return /^tin[:\s_-]*/i.test(trimmed) || (/^\d{10}$/.test(trimmed.replace(/\D/g, "")) && !trimmed.startsWith("+")); +} diff --git a/tsn-cranker-op-daemon/.env.example b/tsn-cranker-op-daemon/.env.example index 30b418f3..be380ab6 100644 --- a/tsn-cranker-op-daemon/.env.example +++ b/tsn-cranker-op-daemon/.env.example @@ -16,3 +16,8 @@ TSN_CRANKER_OPERATOR_FEE_UI=0.02 TSN_CRANKER_SAFETY_MULTIPLIER=1.25 TSN_CRANKER_EXECUTION_LAMPORTS=20000 TSN_CRANKER_ATA_LAMPORTS=2039280 +TSN_SETTLEMENT_TOKEN_MASTER_KEY=replace-with-32-byte-base64-or-64-char-hex-secret +TSN_CRANKER_DNA=trustlink-authorized-cranker +TSN_LOW_LIQUIDITY_THRESHOLD=100 +TSN_RECOVERY_REWARD_BPS=200 +TSN_CRANKER_POLL_SECONDS=2 diff --git a/tsn-cranker-op-daemon/README.md b/tsn-cranker-op-daemon/README.md index 33f06ee1..ff8adf30 100644 --- a/tsn-cranker-op-daemon/README.md +++ b/tsn-cranker-op-daemon/README.md @@ -1,6 +1,6 @@ # TSN Cranker Operator Daemon -This folder is the operator workspace for running a TSN Cranker against your deployed TSN program. +This folder is the operator workspace for running a TSN Cranker against your deployed TSN program. It operates after TINS identity resolution and optional SAS verification; it does not replace either layer. It covers: @@ -71,6 +71,41 @@ Token input behavior: - `npm run setup` supports token symbol or mint input (`USDC` or full mint) - `npm run setup:raw -- init-vault ` also supports symbol or mint + +## Autonomous Python Cranker Runtime + +The TSN OTDT and smart-recovery update (`dfa0735`) adds an autonomous Python scheduler at: + +```text +scripts/cranker_daemon.py +``` + +The daemon monitors the TSN mempool file and automatically processes: + +- intent verification and claim-point accounting, +- claim lease acquisition and OTDT hash issuance, +- in-memory settlement-token decryption for authorized Cranker DNA, +- settlement proof recording and recoverable registry updates, +- recovery queue creation and priority recovery completion, +- liquidity metric updates. + +Run once for validation: + +```bash +TSN_SETTLEMENT_TOKEN_MASTER_KEY=<32-byte-base64-or-64-char-hex-secret> \ +TSN_CRANKER_ONCE=true \ +python scripts/cranker_daemon.py +``` + +Run continuously from this package: + +```bash +TSN_SETTLEMENT_TOKEN_MASTER_KEY=<32-byte-base64-or-64-char-hex-secret> \ +npm run crank:python +``` + +The commitment registry is the public recovery/verification source of truth. It must not store sender wallets, recipient main wallets, phone numbers, token balances, SAS PII, or decrypted settlement tokens. See [`../docs/OTDT-SMART-RECOVERY.md`](../docs/OTDT-SMART-RECOVERY.md) and [`../docs/CRANKER.md`](../docs/CRANKER.md). + ## Step-by-Step Setup (What, Why, Result) For non-crypto operators: diff --git a/tsn-cranker-op-daemon/package.json b/tsn-cranker-op-daemon/package.json index 52725b7b..fb8f62c5 100644 --- a/tsn-cranker-op-daemon/package.json +++ b/tsn-cranker-op-daemon/package.json @@ -12,7 +12,9 @@ "policy:open": "node ./scripts/tsn-setup.mjs set-funding-policy true", "policy:closed": "node ./scripts/tsn-setup.mjs set-funding-policy false", "settle:force": "node ./scripts/tsn-setup.mjs settle-epoch --force", - "crank:start": "tsx ./scripts/cranker.ts" + "crank:start": "tsx ./scripts/cranker.ts", + "crank:python": "python ./scripts/cranker_daemon.py", + "crank:python:once": "TSN_CRANKER_ONCE=true python ./scripts/cranker_daemon.py" }, "dependencies": { "@solana/spl-token": "^0.4.14", diff --git a/tsn-cranker-op-daemon/scripts/cranker_daemon.py b/tsn-cranker-op-daemon/scripts/cranker_daemon.py new file mode 100755 index 00000000..600599a2 --- /dev/null +++ b/tsn-cranker-op-daemon/scripts/cranker_daemon.py @@ -0,0 +1,351 @@ +#!/usr/bin/env python3 +"""Autonomous TSN cranker scheduler for intent, settlement, and recovery work. + +This daemon intentionally uses only commitment-registry fields for settlement +verification. It never writes sender wallets, recipient wallets, or decrypted +settlement tokens to the public registry. +""" + +from __future__ import annotations + +import base64 +import hashlib +import hmac +import json +import os +import time +import uuid +from dataclasses import dataclass +from datetime import datetime, timedelta, timezone +from pathlib import Path +from typing import Any + +TOKEN_ALGORITHM = "TSN-HKDF-SHA256-STREAM-HMAC" + + +def now_iso() -> str: + return datetime.now(timezone.utc).isoformat().replace("+00:00", "Z") + + +def parse_iso(value: str) -> datetime: + return datetime.fromisoformat(value.replace("Z", "+00:00")) + + +def b64url_decode(value: str) -> bytes: + padded = value.replace("-", "+").replace("_", "/") + "=" * ((4 - len(value) % 4) % 4) + return base64.b64decode(padded) + + +def b64url_encode(value: bytes) -> str: + return base64.b64encode(value).decode().rstrip("=").replace("+", "-").replace("/", "_") + + +def sha256_hex(value: str | bytes) -> str: + if isinstance(value, str): + value = value.encode() + return hashlib.sha256(value).hexdigest() + + +def canonical_json(value: Any) -> str: + return json.dumps(value, sort_keys=True, separators=(",", ":")) + + +def normalize_master_key() -> bytes: + raw = os.environ.get("TSN_SETTLEMENT_TOKEN_MASTER_KEY", "") + if not raw: + raise RuntimeError("TSN_SETTLEMENT_TOKEN_MASTER_KEY is required for OTDT settlement decryption") + raw = raw.strip() + if len(raw) == 64 and all(char in "0123456789abcdefABCDEF" for char in raw): + return bytes.fromhex(raw) + try: + decoded = base64.b64decode(raw) + if len(decoded) >= 32: + return decoded + except Exception: + pass + return bytes.fromhex(sha256_hex(raw)) + + +def hmac_sha256(key: bytes, value: str | bytes) -> bytes: + if isinstance(value, str): + value = value.encode() + return hmac.new(key, value, hashlib.sha256).digest() + + +def derive_keys(master_key: bytes, transfer_id: str, salt: str, dna_hash: str) -> tuple[bytes, bytes]: + prk = hmac_sha256(master_key, f"tsn-settlement:{transfer_id}:{salt}") + return hmac_sha256(prk, f"enc:{dna_hash}"), hmac_sha256(prk, f"mac:{dna_hash}") + + +def stream_xor(data: bytes, key: bytes, nonce: bytes) -> bytes: + output = bytearray(len(data)) + offset = 0 + counter = 0 + while offset < len(data): + block = hmac_sha256(key, nonce + counter.to_bytes(4, "big")) + for byte in block: + if offset >= len(data): + break + output[offset] = data[offset] ^ byte + offset += 1 + counter += 1 + return bytes(output) + + +def decrypt_settlement_token(encrypted_token: str, transfer_id: str, commitment_hash: str, dna_hash: str) -> dict[str, Any]: + envelope = json.loads(b64url_decode(encrypted_token).decode()) + if envelope.get("algorithm") != TOKEN_ALGORITHM: + raise RuntimeError("Unsupported settlement token algorithm") + if envelope.get("authorizedCrankerDnaHash") != dna_hash: + raise RuntimeError("Cranker DNA is not authorized for this settlement token") + aad = b64url_decode(envelope["aad"]).decode() + if sha256_hex(aad) != envelope.get("aadHash"): + raise RuntimeError("Settlement token AAD hash mismatch") + enc_key, mac_key = derive_keys(normalize_master_key(), transfer_id, envelope["salt"], dna_hash) + nonce = b64url_decode(envelope["nonce"]) + ciphertext = b64url_decode(envelope["ciphertext"]) + tag = hmac_sha256(mac_key, aad.encode() + nonce + ciphertext) + if not hmac.compare_digest(tag, b64url_decode(envelope["tag"])): + raise RuntimeError("Settlement token authentication failed") + plaintext = json.loads(stream_xor(ciphertext, enc_key, nonce).decode()) + if plaintext.get("transferId") != transfer_id: + raise RuntimeError("Settlement token transfer mismatch") + if sha256_hex(canonical_json(plaintext)) != commitment_hash: + raise RuntimeError("Settlement token commitment mismatch") + return plaintext + + +@dataclass +class SchedulerConfig: + mempool_file: Path + cranker_pubkey: str + dna_hash: str + poll_seconds: float + low_liquidity_threshold: float + + +class TsnScheduler: + def __init__(self, config: SchedulerConfig): + self.config = config + + def load(self) -> dict[str, Any]: + if not self.config.mempool_file.exists(): + return self.normalize({"intents": [], "claimRequests": [], "proofs": []}) + with self.config.mempool_file.open("r", encoding="utf8") as handle: + return self.normalize(json.load(handle)) + + def save(self, state: dict[str, Any]) -> None: + self.config.mempool_file.parent.mkdir(parents=True, exist_ok=True) + tmp = self.config.mempool_file.with_suffix(".tmp") + with tmp.open("w", encoding="utf8") as handle: + json.dump(self.normalize(state), handle, indent=2) + handle.write("\n") + tmp.replace(self.config.mempool_file) + + def normalize(self, state: dict[str, Any]) -> dict[str, Any]: + state.setdefault("intents", []) + state.setdefault("claimRequests", []) + state.setdefault("proofs", []) + state.setdefault("commitmentRegistry", []) + state.setdefault("claimPointLedger", []) + state.setdefault("claimLeases", []) + state.setdefault("recoveryQueue", []) + state.setdefault( + "liquidityMetrics", + { + "activeLiquidity": float(os.environ.get("TSN_ACTIVE_LIQUIDITY", "0")), + "pendingIntentAmount": 0, + "vaultBalance": float(os.environ.get("TSN_VAULT_BALANCE", "0")), + "settlementVelocity": 0, + "liquidityConsumptionRate": 0, + "lowLiquidityThreshold": self.config.low_liquidity_threshold, + "updatedAt": now_iso(), + }, + ) + return state + + def ledger(self, state: dict[str, Any]) -> dict[str, Any]: + for entry in state["claimPointLedger"]: + if entry["crankerPubkey"] == self.config.cranker_pubkey: + return entry + entry = {"crankerPubkey": self.config.cranker_pubkey, "earned": 0, "available": 0, "leased": 0, "lastIntentWorkAt": None} + state["claimPointLedger"].append(entry) + return entry + + def registry_entry(self, state: dict[str, Any], transfer_id: str) -> dict[str, Any] | None: + return next((entry for entry in state["commitmentRegistry"] if entry["transferId"] == transfer_id), None) + + def refresh_metrics(self, state: dict[str, Any]) -> dict[str, Any]: + metrics = state["liquidityMetrics"] + pending = sum(float(intent.get("amount") or 0) for intent in state["intents"] if intent.get("status") in {"pending", "escrowed", "onchain", "claimed"}) + active = max(0.0, float(metrics.get("activeLiquidity") or 0)) + executed = sum(1 for intent in state["intents"] if intent.get("status") in {"executed", "settled"}) + metrics.update( + { + "pendingIntentAmount": pending, + "settlementVelocity": executed, + "liquidityConsumptionRate": pending / active if active else pending, + "lowLiquidityThreshold": self.config.low_liquidity_threshold, + "updatedAt": now_iso(), + } + ) + return metrics + + def verify_intents(self, state: dict[str, Any]) -> int: + completed = 0 + for intent in state["intents"]: + if intent.get("status") != "pending": + continue + if not intent.get("encryptedSettlementToken") or not intent.get("settlementTokenCommitmentHash"): + intent.update({"status": "failed", "settlementReason": "missing settlement token commitment", "updatedAt": now_iso()}) + continue + if not self.registry_entry(state, intent["id"]): + state["commitmentRegistry"].append( + { + "transferId": intent["id"], + "encryptedSettlementToken": intent["encryptedSettlementToken"], + "commitmentHash": intent["settlementTokenCommitmentHash"], + "timestamp": intent.get("postedAt") or now_iso(), + "epoch": intent.get("epoch", int(time.time() // 3600)), + "recoverable": False, + "intentVerifierPubkey": self.config.cranker_pubkey, + "updatedAt": now_iso(), + } + ) + intent.update({"status": "escrowed", "assignedCrankerPubkey": self.config.cranker_pubkey, "updatedAt": now_iso()}) + ledger = self.ledger(state) + ledger["earned"] += 1 + ledger["available"] += 1 + ledger["lastIntentWorkAt"] = now_iso() + completed += 1 + return completed + + def acquire_lease(self, state: dict[str, Any], transfer_id: str) -> dict[str, Any] | None: + entry = self.registry_entry(state, transfer_id) + if not entry or entry.get("recoverable") or entry.get("otdtHash"): + return None + for lease in state["claimLeases"]: + if lease["transferId"] == transfer_id and lease["status"] == "active" and parse_iso(lease["expiresAt"]) > datetime.now(timezone.utc): + return lease if lease["crankerPubkey"] == self.config.cranker_pubkey else None + ledger = self.ledger(state) + if ledger["available"] < 1: + return None + ledger["available"] -= 1 + ledger["leased"] += 1 + issued_at = datetime.now(timezone.utc) + lease_id = str(uuid.uuid4()) + otdt_hash = sha256_hex(f"{transfer_id}:{lease_id}:{self.config.cranker_pubkey}:{entry['commitmentHash']}:{uuid.uuid4().hex}") + lease = { + "id": lease_id, + "transferId": transfer_id, + "crankerPubkey": self.config.cranker_pubkey, + "status": "active", + "pointsSpent": 1, + "otdtHash": otdt_hash, + "issuedAt": issued_at.isoformat().replace("+00:00", "Z"), + "expiresAt": (issued_at + timedelta(minutes=10)).isoformat().replace("+00:00", "Z"), + } + entry["otdtHash"] = otdt_hash + entry["updatedAt"] = now_iso() + state["claimLeases"].append(lease) + return lease + + def settle_claims(self, state: dict[str, Any]) -> int: + completed = 0 + for claim in state["claimRequests"]: + if claim.get("status") != "pending": + continue + intent = next((item for item in state["intents"] if item["id"] == claim["intentId"] and item.get("status") in {"escrowed", "onchain", "claimed"}), None) + if not intent: + continue + entry = self.registry_entry(state, intent["id"]) + lease = self.acquire_lease(state, intent["id"]) + if not entry or not lease: + continue + token = decrypt_settlement_token(intent["encryptedSettlementToken"], intent["id"], entry["commitmentHash"], self.config.dna_hash) + settlement_commitment = sha256_hex(canonical_json({"transferId": token["transferId"], "commitmentHash": entry["commitmentHash"], "leaseId": lease["id"], "crankerPubkey": self.config.cranker_pubkey})) + proof_tx = f"simulated-settlement-{uuid.uuid4().hex}" + entry.update({"recoverable": True, "settlementCommitmentHash": settlement_commitment, "settlementProofTx": proof_tx, "updatedAt": now_iso()}) + intent.update({"status": "executed", "proofTxSig": proof_tx, "claimLeaseId": lease["id"], "updatedAt": now_iso()}) + claim.update({"status": "completed", "claimLeaseId": lease["id"], "updatedAt": now_iso()}) + lease.update({"status": "completed", "completedAt": now_iso()}) + state["proofs"].append({"intent_id": intent["id"], "timestamp": now_iso(), "cranker_pubkey": self.config.cranker_pubkey, "proof_tx": proof_tx, "settlement_commitment_hash": settlement_commitment, "otdt_hash": lease["otdtHash"]}) + self.enqueue_recovery(state, intent, entry) + completed += 1 + return completed + + def enqueue_recovery(self, state: dict[str, Any], intent: dict[str, Any], entry: dict[str, Any]) -> None: + if any(job["transferId"] == intent["id"] for job in state["recoveryQueue"]): + return + metrics = self.refresh_metrics(state) + amount = float(intent.get("amount") or 0) + deficit = max(0.0, self.config.low_liquidity_threshold - float(metrics.get("activeLiquidity") or 0)) + priority = round(deficit * 10 + float(metrics.get("pendingIntentAmount") or 0) * 2 + amount, 6) + state["recoveryQueue"].append( + { + "id": str(uuid.uuid4()), + "transferId": intent["id"], + "epoch": entry["epoch"], + "recoverableAmount": amount, + "vaultSource": f"commitment:{entry['commitmentHash']}", + "recoveryReward": round(amount * 0.02, 9), + "priorityScore": priority, + "status": "open", + "createdAt": now_iso(), + "updatedAt": now_iso(), + } + ) + + def recover_liquidity(self, state: dict[str, Any]) -> int: + metrics = self.refresh_metrics(state) + open_jobs = sorted((job for job in state["recoveryQueue"] if job.get("status") == "open"), key=lambda job: (-float(job.get("priorityScore") or 0), job["createdAt"])) + completed = 0 + for job in open_jobs: + if float(metrics.get("activeLiquidity") or 0) >= self.config.low_liquidity_threshold and float(job.get("priorityScore") or 0) <= 0: + continue + proof_tx = f"simulated-recovery-{uuid.uuid4().hex}" + job.update({"status": "completed", "leasedByCrankerPubkey": self.config.cranker_pubkey, "proofTx": proof_tx, "updatedAt": now_iso()}) + entry = self.registry_entry(state, job["transferId"]) + if entry: + entry.update({"recoveryProofTx": proof_tx, "updatedAt": now_iso()}) + metrics["activeLiquidity"] = float(metrics.get("activeLiquidity") or 0) + float(job.get("recoverableAmount") or 0) + metrics["vaultBalance"] = float(metrics.get("vaultBalance") or 0) + float(job.get("recoverableAmount") or 0) + metrics["updatedAt"] = now_iso() + completed += 1 + return completed + + def tick(self) -> tuple[int, int, int]: + state = self.load() + intent_count = self.verify_intents(state) + settlement_count = self.settle_claims(state) + recovery_count = self.recover_liquidity(state) + self.refresh_metrics(state) + self.save(state) + return intent_count, settlement_count, recovery_count + + def run(self) -> None: + while True: + counts = self.tick() + if any(counts): + print(f"[tsn-python-cranker] intent={counts[0]} settlement={counts[1]} recovery={counts[2]}", flush=True) + time.sleep(self.config.poll_seconds) + + +def load_config() -> SchedulerConfig: + pubkey = os.environ.get("TSN_CRANKER_PUBKEY") or os.environ.get("TSN_CRANKER_OPERATOR_PUBKEY") or "local-python-cranker" + dna = os.environ.get("TSN_CRANKER_DNA", "trustlink-authorized-cranker") + return SchedulerConfig( + mempool_file=Path(os.environ.get("TSN_MEMPOOL_FILE", ".tsn/mempool.json")).resolve(), + cranker_pubkey=pubkey, + dna_hash=sha256_hex(f"tsn-cranker-dna:{dna}"), + poll_seconds=float(os.environ.get("TSN_CRANKER_POLL_SECONDS", "2")), + low_liquidity_threshold=float(os.environ.get("TSN_LOW_LIQUIDITY_THRESHOLD", "100")), + ) + + +if __name__ == "__main__": + scheduler = TsnScheduler(load_config()) + if os.environ.get("TSN_CRANKER_ONCE") == "true": + print(scheduler.tick()) + else: + scheduler.run() diff --git a/tsn-sdk/dist/client.d.ts b/tsn-sdk/dist/client.d.ts index 9e0417e5..44cdf7b4 100644 --- a/tsn-sdk/dist/client.d.ts +++ b/tsn-sdk/dist/client.d.ts @@ -12,6 +12,7 @@ export declare class TsnHttpClient { postIntent(body: TRequest): Promise; postClaimRequest(body: TRequest): Promise; listPendingWork(limit?: number): Promise; + listPendingIntentWork(limit?: number): Promise; updateIntentStatus(id: string, body: TRequest): Promise; updateClaimRequestStatus(id: string, body: TRequest): Promise; postProof(body: TRequest): Promise; diff --git a/tsn-sdk/dist/contracts.d.ts b/tsn-sdk/dist/contracts.d.ts index d908d463..50101af8 100644 --- a/tsn-sdk/dist/contracts.d.ts +++ b/tsn-sdk/dist/contracts.d.ts @@ -1,5 +1,7 @@ export type TsnIntentStatus = "pending" | "escrowed" | "onchain" | "claimed" | "executed" | "settled" | "expired" | "failed" | "canceled" | "reverted"; export type TsnClaimRequestStatus = "pending" | "processing" | "completed" | "failed" | "canceled"; +export type TsnLeaseStatus = "active" | "completed" | "expired" | "canceled"; +export type TsnRecoveryJobStatus = "open" | "leased" | "completed" | "failed" | "canceled"; export type TsnUiStage = "intent_pending" | "claim_requested" | "escrowed" | "lease_claimed" | "cranker_paid" | "epoch_settled" | "reverted"; export type PaymentIntentStatus = "pending" | "escrowed" | "onchain" | "claimed" | "executed" | "settled" | "expired" | "failed" | "canceled" | "reverted"; export type ClaimRequestStatus = "pending" | "processing" | "completed" | "canceled" | "failed"; @@ -52,6 +54,66 @@ export type CreateIntentRequest = { amount: number; recipientAmount?: number; source?: string; + epoch?: number; + encryptedSettlementToken?: string; + settlementTokenCommitmentHash?: string; + commitmentRegistryEntry?: CommitmentRegistryEntry; +}; +export type CommitmentRegistryEntry = { + transferId: string; + encryptedSettlementToken: string; + commitmentHash: string; + timestamp: string; + epoch: number; + recoverable: boolean; + intentVerifierPubkey?: string | null; + settlementCommitmentHash?: string | null; + settlementProofTx?: string | null; + otdtHash?: string | null; + recoveryProofTx?: string | null; + updatedAt?: string; +}; +export type ClaimPointLedgerEntry = { + crankerPubkey: string; + earned: number; + available: number; + leased: number; + lastIntentWorkAt?: string | null; +}; +export type ClaimLeaseRecord = { + id: string; + transferId: string; + crankerPubkey: string; + status: TsnLeaseStatus; + pointsSpent: number; + otdtHash?: string | null; + issuedAt: string; + expiresAt: string; + completedAt?: string | null; +}; +export type RecoveryQueueEntry = { + id: string; + transferId: string; + epoch: number; + recoverableAmount: number; + vaultSource: string; + recoveryReward: number; + priorityScore: number; + status: TsnRecoveryJobStatus; + leasedByCrankerPubkey?: string | null; + leaseExpiresAt?: string | null; + proofTx?: string | null; + createdAt: string; + updatedAt: string; +}; +export type LiquidityMetrics = { + activeLiquidity: number; + pendingIntentAmount: number; + vaultBalance: number; + settlementVelocity: number; + liquidityConsumptionRate: number; + lowLiquidityThreshold: number; + updatedAt: string; }; export type RequestClaimRequest = { paymentId: string; @@ -70,6 +132,7 @@ export type TsnMempoolIntent = CreateIntentRequest & { proofTxSig?: string | null; settlementResolution?: "completed" | "reverted" | null; settlementReason?: string | null; + claimLeaseId?: string | null; postedAt: string; updatedAt: string; }; @@ -77,6 +140,7 @@ export type TsnMempoolClaimRequest = RequestClaimRequest & { id: string; status: TsnClaimRequestStatus; settlementReason?: string | null; + claimLeaseId?: string | null; postedAt: string; updatedAt: string; }; @@ -93,6 +157,8 @@ export type ProofOfPaymentRequest = { cranker_pubkey: string; proof_tx: string; encrypted_payload?: string | null; + settlement_commitment_hash?: string | null; + otdt_hash?: string | null; }; export type IntentState = { status: TsnIntentStatus; @@ -122,6 +188,9 @@ export declare function buildCreateIntentRequest(params: { tokenMintAddress: string; amount: number; source?: string; + epoch?: number; + encryptedSettlementToken?: string; + settlementTokenCommitmentHash?: string; }): CreateIntentRequest; export declare function buildRequestClaimRequest(params: RequestClaimRequest): RequestClaimRequest; export declare function computeTsnUiStage(intent: IntentState, claimRequest: ClaimRequestState): TsnUiStage; diff --git a/tsn-sdk/dist/contracts.d.ts.map b/tsn-sdk/dist/contracts.d.ts.map index 01a04431..2105a0bb 100644 --- a/tsn-sdk/dist/contracts.d.ts.map +++ b/tsn-sdk/dist/contracts.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"contracts.d.ts","sourceRoot":"","sources":["../src/contracts.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"contracts.d.ts","sourceRoot":"","sources":["../src/contracts.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,CAAC;AACvJ,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,YAAY,GAAG,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;AACnG,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,WAAW,GAAG,SAAS,GAAG,UAAU,CAAC;AAC7E,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;AAC3F,MAAM,MAAM,UAAU,GAAG,gBAAgB,GAAG,iBAAiB,GAAG,UAAU,GAAG,eAAe,GAAG,cAAc,GAAG,eAAe,GAAG,UAAU,CAAC;AAE7I,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,CAAC;AAC3J,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,YAAY,GAAG,WAAW,GAAG,UAAU,GAAG,QAAQ,CAAC;AAEhG,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,mBAAmB,CAAC;IAC5B,uBAAuB,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,0BAA0B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,4BAA4B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C,wBAAwB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,2BAA2B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C,4BAA4B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,iCAAiC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClD,8BAA8B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,oBAAoB,CAAC,EAAE,2BAA2B,GAAG,MAAM,GAAG,IAAI,CAAC;IACnE,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,sBAAsB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,yBAAyB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,6BAA6B,CAAC,EAAE,MAAM,CAAC;IACvC,uBAAuB,CAAC,EAAE,uBAAuB,CAAC;CACnD,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,wBAAwB,EAAE,MAAM,CAAC;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,OAAO,CAAC;IACrB,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,wBAAwB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,cAAc,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,oBAAoB,CAAC;IAC7B,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,eAAe,EAAE,MAAM,CAAC;IACxB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,wBAAwB,EAAE,MAAM,CAAC;IACjC,qBAAqB,EAAE,MAAM,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,mBAAmB,GAAG;IACnD,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,eAAe,CAAC;IACxB,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,oBAAoB,CAAC,EAAE,WAAW,GAAG,UAAU,GAAG,IAAI,CAAC;IACvD,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG,mBAAmB,GAAG;IACzD,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,qBAAqB,CAAC;IAC9B,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,gBAAgB,CAAC;IACzB,YAAY,EAAE,sBAAsB,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,gBAAgB,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,0BAA0B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,eAAe,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,qBAAqB,CAAC;CAC/B,GAAG,IAAI,CAAC;AAET,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,0BAA0B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,4BAA4B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C,wBAAwB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,2BAA2B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C,4BAA4B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,iCAAiC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClD,8BAA8B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,oBAAoB,CAAC,EAAE,2BAA2B,GAAG,MAAM,GAAG,IAAI,CAAC;IACnE,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,sBAAsB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,yBAAyB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,6BAA6B,CAAC,EAAE,MAAM,CAAC;CACxC,GAAG,mBAAmB,CA2BtB;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,mBAAmB,GAAG,mBAAmB,CAEzF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,iBAAiB,GAAG,UAAU,CAclG"} \ No newline at end of file diff --git a/tsn-sdk/dist/contracts.js b/tsn-sdk/dist/contracts.js index 45a00522..ff58e23e 100644 --- a/tsn-sdk/dist/contracts.js +++ b/tsn-sdk/dist/contracts.js @@ -26,6 +26,9 @@ export function buildCreateIntentRequest(params) { tokenMintAddress: params.tokenMintAddress, amount: params.amount, source: params.source, + epoch: params.epoch, + encryptedSettlementToken: params.encryptedSettlementToken, + settlementTokenCommitmentHash: params.settlementTokenCommitmentHash, }; } export function buildRequestClaimRequest(params) { diff --git a/tsn-sdk/dist/index.d.ts b/tsn-sdk/dist/index.d.ts index 338a8cf8..8ab37333 100644 --- a/tsn-sdk/dist/index.d.ts +++ b/tsn-sdk/dist/index.d.ts @@ -13,4 +13,5 @@ export * from "./sponsored-settlement.js"; export * from "./tins.js"; export * from "./blockchain/solana-core.js"; export * from "./blockchain/solana-tsn.js"; +export * from "./settlement-token.js"; //# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/tsn-sdk/dist/index.d.ts.map b/tsn-sdk/dist/index.d.ts.map index 748cfe36..71f563db 100644 --- a/tsn-sdk/dist/index.d.ts.map +++ b/tsn-sdk/dist/index.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AACpC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,mCAAmC,CAAC;AAClD,cAAc,mBAAmB,CAAC;AAClC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,WAAW,CAAC;AAC1B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC"} \ No newline at end of file +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AACpC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,mCAAmC,CAAC;AAClD,cAAc,mBAAmB,CAAC;AAClC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,WAAW,CAAC;AAC1B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uBAAuB,CAAC"} \ No newline at end of file diff --git a/tsn-sdk/dist/index.js b/tsn-sdk/dist/index.js index 119e2fe2..a0b5d723 100644 --- a/tsn-sdk/dist/index.js +++ b/tsn-sdk/dist/index.js @@ -15,3 +15,4 @@ export * from "./sponsored-settlement.js"; export * from "./tins.js"; export * from "./blockchain/solana-core.js"; export * from "./blockchain/solana-tsn.js"; +export * from "./settlement-token.js"; diff --git a/tsn-sdk/dist/mempool.d.ts b/tsn-sdk/dist/mempool.d.ts index c80065fc..d159421c 100644 --- a/tsn-sdk/dist/mempool.d.ts +++ b/tsn-sdk/dist/mempool.d.ts @@ -1,4 +1,4 @@ -import type { CreateIntentRequest, RequestClaimRequest, TsnClaimRequestStatus, TsnIntentStatus, TsnMempoolClaimRequest, TsnMempoolIntent, TsnIntentWorkItem, TsnWorkItem, ProofOfPaymentRequest } from "./contracts.js"; +import type { ClaimLeaseRecord, ClaimPointLedgerEntry, CommitmentRegistryEntry, CreateIntentRequest, LiquidityMetrics, ProofOfPaymentRequest, RecoveryQueueEntry, RequestClaimRequest, TsnClaimRequestStatus, TsnIntentStatus, TsnMempoolClaimRequest, TsnMempoolIntent, TsnIntentWorkItem, TsnWorkItem } from "./contracts.js"; export interface TsnMempool { postIntent(request: CreateIntentRequest): Promise; postClaimRequest(request: RequestClaimRequest): Promise; @@ -11,6 +11,16 @@ export interface TsnMempool { }): Promise; listPendingIntentWork(limit?: number): Promise; listPendingWork(limit?: number): Promise; + listCommitmentRegistry(): Promise; + listClaimPointLedger(): Promise; + listClaimLeases(): Promise; + listRecoveryQueue(params?: { + status?: RecoveryQueueEntry["status"]; + }): Promise; + getLiquidityMetrics(): Promise; + submitIntentVerification(id: string, crankerPubkey: string, patch?: Partial): Promise; + acquireClaimLease(intentId: string, crankerPubkey: string): Promise; + completeRecoveryJob(jobId: string, crankerPubkey: string, proofTx: string): Promise; updateIntentStatus(id: string, status: TsnIntentStatus, patch?: Partial): Promise; updateClaimRequestStatus(id: string, status: TsnClaimRequestStatus, patch?: Partial): Promise; postProof(request: ProofOfPaymentRequest): Promise; @@ -29,9 +39,19 @@ export declare class JsonFileTsnMempool implements TsnMempool { }): Promise; listPendingWork(limit?: number): Promise; listPendingIntentWork(limit?: number): Promise; + listCommitmentRegistry(): Promise; + listClaimPointLedger(): Promise; + listClaimLeases(): Promise; + listRecoveryQueue(params?: { + status?: RecoveryQueueEntry["status"]; + }): Promise; + getLiquidityMetrics(): Promise; + submitIntentVerification(id: string, crankerPubkey: string, patch?: Partial): Promise; + acquireClaimLease(intentId: string, crankerPubkey: string): Promise; updateIntentStatus(id: string, status: TsnIntentStatus, patch?: Partial): Promise; updateClaimRequestStatus(id: string, status: TsnClaimRequestStatus, patch?: Partial): Promise; postProof(request: ProofOfPaymentRequest): Promise; + completeRecoveryJob(jobId: string, crankerPubkey: string, proofTx: string): Promise; } export declare class HttpTsnMempool implements TsnMempool { private readonly client; @@ -47,6 +67,16 @@ export declare class HttpTsnMempool implements TsnMempool { }): Promise; listPendingWork(limit?: number): Promise; listPendingIntentWork(limit?: number): Promise; + listCommitmentRegistry(): Promise; + listClaimPointLedger(): Promise; + listClaimLeases(): Promise; + listRecoveryQueue(params?: { + status?: RecoveryQueueEntry["status"]; + }): Promise; + getLiquidityMetrics(): Promise; + submitIntentVerification(id: string, crankerPubkey: string, patch?: Partial): Promise; + acquireClaimLease(intentId: string, crankerPubkey: string): Promise; + completeRecoveryJob(jobId: string, crankerPubkey: string, proofTx: string): Promise; updateIntentStatus(id: string, status: TsnIntentStatus, patch?: Partial): Promise; updateClaimRequestStatus(id: string, status: TsnClaimRequestStatus, patch?: Partial): Promise; postProof(request: ProofOfPaymentRequest): Promise; diff --git a/tsn-sdk/dist/mempool.d.ts.map b/tsn-sdk/dist/mempool.d.ts.map index 779906f3..752dd481 100644 --- a/tsn-sdk/dist/mempool.d.ts.map +++ b/tsn-sdk/dist/mempool.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"mempool.d.ts","sourceRoot":"","sources":["../src/mempool.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"mempool.d.ts","sourceRoot":"","sources":["../src/mempool.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/tsn-sdk/dist/mempool.js b/tsn-sdk/dist/mempool.js index 595c507a..677d7903 100644 --- a/tsn-sdk/dist/mempool.js +++ b/tsn-sdk/dist/mempool.js @@ -2,23 +2,108 @@ import { randomUUID } from "node:crypto"; import { mkdir, readFile, writeFile } from "node:fs/promises"; import { dirname, resolve } from "node:path"; import { TsnHttpClient } from "./client.js"; +import { createEncryptedSettlementToken, createOneTimeDecryptionToken, currentTsnEpoch, settlementSha256Hex, } from "./settlement-token.js"; function now() { return new Date().toISOString(); } +function defaultLiquidityMetrics() { + return { + activeLiquidity: Number(process.env.TSN_ACTIVE_LIQUIDITY ?? 0), + pendingIntentAmount: 0, + vaultBalance: Number(process.env.TSN_VAULT_BALANCE ?? 0), + settlementVelocity: 0, + liquidityConsumptionRate: 0, + lowLiquidityThreshold: Number(process.env.TSN_LOW_LIQUIDITY_THRESHOLD ?? 100), + updatedAt: now(), + }; +} +function normalizeSnapshot(snapshot) { + snapshot.proofs ??= []; + snapshot.commitmentRegistry ??= []; + snapshot.claimPointLedger ??= []; + snapshot.claimLeases ??= []; + snapshot.recoveryQueue ??= []; + snapshot.liquidityMetrics ??= defaultLiquidityMetrics(); + return snapshot; +} async function readSnapshot(path) { try { - return JSON.parse(await readFile(path, "utf8")); + return normalizeSnapshot(JSON.parse(await readFile(path, "utf8"))); } catch (error) { if (error.code === "ENOENT") { - return { intents: [], claimRequests: [], proofs: [] }; + return normalizeSnapshot({ intents: [], claimRequests: [], proofs: [] }); } throw error; } } async function writeSnapshot(path, snapshot) { await mkdir(dirname(path), { recursive: true }); - await writeFile(path, `${JSON.stringify(snapshot, null, 2)}\n`, "utf8"); + await writeFile(path, `${JSON.stringify(normalizeSnapshot(snapshot), null, 2)}\n`, "utf8"); +} +function ensureSettlementToken(request) { + if (request.encryptedSettlementToken && request.settlementTokenCommitmentHash) + return request; + const epoch = request.epoch ?? currentTsnEpoch(); + const bundle = createEncryptedSettlementToken({ + transferId: request.paymentId, + recipientHash: request.recipientHash, + tokenMintAddress: request.tokenMintAddress, + amount: request.amount, + epoch, + }); + return { + ...request, + epoch, + encryptedSettlementToken: bundle.encryptedSettlementToken, + settlementTokenCommitmentHash: bundle.commitmentHash, + }; +} +function registryEntryForIntent(intent) { + if (!intent.encryptedSettlementToken || !intent.settlementTokenCommitmentHash) { + throw new Error("TSN intent is missing encrypted settlement token commitment"); + } + return { + transferId: intent.id, + encryptedSettlementToken: intent.encryptedSettlementToken, + commitmentHash: intent.settlementTokenCommitmentHash, + timestamp: intent.postedAt, + epoch: intent.epoch ?? currentTsnEpoch(), + recoverable: false, + updatedAt: now(), + }; +} +function getLedger(snapshot, crankerPubkey) { + snapshot.claimPointLedger ??= []; + let entry = snapshot.claimPointLedger.find((candidate) => candidate.crankerPubkey === crankerPubkey); + if (!entry) { + entry = { crankerPubkey, earned: 0, available: 0, leased: 0, lastIntentWorkAt: null }; + snapshot.claimPointLedger.push(entry); + } + return entry; +} +function refreshLiquidityMetrics(snapshot) { + snapshot.liquidityMetrics ??= defaultLiquidityMetrics(); + const pendingIntentAmount = snapshot.intents + .filter((intent) => ["pending", "escrowed", "onchain", "claimed"].includes(intent.status)) + .reduce((total, intent) => total + Number(intent.amount || 0), 0); + const executedCount = snapshot.intents.filter((intent) => intent.status === "executed" || intent.status === "settled").length; + const activeLiquidity = Math.max(0, Number(snapshot.liquidityMetrics.activeLiquidity || 0)); + const vaultBalance = Math.max(0, Number(snapshot.liquidityMetrics.vaultBalance || activeLiquidity)); + snapshot.liquidityMetrics = { + ...snapshot.liquidityMetrics, + activeLiquidity, + vaultBalance, + pendingIntentAmount, + settlementVelocity: executedCount, + liquidityConsumptionRate: activeLiquidity > 0 ? pendingIntentAmount / activeLiquidity : pendingIntentAmount, + updatedAt: now(), + }; + return snapshot.liquidityMetrics; +} +function recoveryPriority(metrics, jobAmount) { + const deficit = Math.max(0, metrics.lowLiquidityThreshold - metrics.activeLiquidity); + return Number((deficit * 10 + metrics.pendingIntentAmount * 2 + metrics.settlementVelocity + jobAmount).toFixed(6)); } export class JsonFileTsnMempool { path; @@ -31,14 +116,17 @@ export class JsonFileTsnMempool { if (existing) return existing; const timestamp = now(); + const securedRequest = ensureSettlementToken(request); const intent = { - ...request, - id: request.paymentId, + ...securedRequest, + id: securedRequest.paymentId, status: "pending", postedAt: timestamp, updatedAt: timestamp, }; snapshot.intents.push(intent); + snapshot.commitmentRegistry.push(registryEntryForIntent(intent)); + refreshLiquidityMetrics(snapshot); await writeSnapshot(this.path, snapshot); return intent; } @@ -56,6 +144,7 @@ export class JsonFileTsnMempool { updatedAt: timestamp, }; snapshot.claimRequests.push(claimRequest); + refreshLiquidityMetrics(snapshot); await writeSnapshot(this.path, snapshot); return claimRequest; } @@ -96,12 +185,99 @@ export class JsonFileTsnMempool { .slice(0, limit) .map((intent) => ({ intent })); } + async listCommitmentRegistry() { + return (await readSnapshot(this.path)).commitmentRegistry; + } + async listClaimPointLedger() { + return (await readSnapshot(this.path)).claimPointLedger; + } + async listClaimLeases() { + return (await readSnapshot(this.path)).claimLeases; + } + async listRecoveryQueue(params = {}) { + const queue = (await readSnapshot(this.path)).recoveryQueue; + return params.status ? queue.filter((entry) => entry.status === params.status) : queue; + } + async getLiquidityMetrics() { + const snapshot = await readSnapshot(this.path); + const metrics = refreshLiquidityMetrics(snapshot); + await writeSnapshot(this.path, snapshot); + return metrics; + } + async submitIntentVerification(id, crankerPubkey, patch = {}) { + const snapshot = await readSnapshot(this.path); + const intent = snapshot.intents.find((candidate) => candidate.id === id); + if (!intent) + return null; + if (!intent.encryptedSettlementToken || !intent.settlementTokenCommitmentHash) { + throw new Error("Intent verification failed: encrypted settlement token commitment is missing"); + } + const registryEntry = snapshot.commitmentRegistry.find((entry) => entry.transferId === id); + if (!registryEntry) + snapshot.commitmentRegistry.push(registryEntryForIntent(intent)); + Object.assign(intent, patch, { + status: "escrowed", + assignedCrankerPubkey: crankerPubkey, + updatedAt: now(), + }); + const ledger = getLedger(snapshot, crankerPubkey); + ledger.earned += 1; + ledger.available += 1; + ledger.lastIntentWorkAt = now(); + refreshLiquidityMetrics(snapshot); + await writeSnapshot(this.path, snapshot); + return intent; + } + async acquireClaimLease(intentId, crankerPubkey) { + const snapshot = await readSnapshot(this.path); + const registryEntry = snapshot.commitmentRegistry.find((entry) => entry.transferId === intentId); + if (!registryEntry) + throw new Error("Commitment registry entry not found"); + if (registryEntry.recoverable) + throw new Error("Transfer is already recoverable; no claim lease is permitted"); + if (registryEntry.otdtHash) + throw new Error("OTDT has already been issued for this transfer"); + const existingLease = snapshot.claimLeases.find((lease) => lease.transferId === intentId && lease.status === "active" && Date.parse(lease.expiresAt) > Date.now()); + if (existingLease) + return existingLease; + const ledger = getLedger(snapshot, crankerPubkey); + if (ledger.available < 1) + throw new Error("Cranker has no available claim points for a claim lease"); + ledger.available -= 1; + ledger.leased += 1; + const issuedAt = now(); + const lease = { + id: randomUUID(), + transferId: intentId, + crankerPubkey, + status: "active", + pointsSpent: 1, + issuedAt, + expiresAt: new Date(Date.parse(issuedAt) + Number(process.env.TSN_CLAIM_LEASE_TTL_MS ?? 10 * 60_000)).toISOString(), + }; + const otdt = createOneTimeDecryptionToken({ + transferId: intentId, + leaseId: lease.id, + crankerPubkey, + commitmentHash: registryEntry.commitmentHash, + }); + lease.otdtHash = otdt.tokenHash; + registryEntry.otdtHash = otdt.tokenHash; + registryEntry.updatedAt = now(); + snapshot.claimLeases.push(lease); + const intent = snapshot.intents.find((candidate) => candidate.id === intentId); + if (intent) + Object.assign(intent, { status: "claimed", claimLeaseId: lease.id, updatedAt: now() }); + await writeSnapshot(this.path, snapshot); + return lease; + } async updateIntentStatus(id, status, patch = {}) { const snapshot = await readSnapshot(this.path); const intent = snapshot.intents.find((candidate) => candidate.id === id); if (!intent) return null; Object.assign(intent, patch, { status, updatedAt: now() }); + refreshLiquidityMetrics(snapshot); await writeSnapshot(this.path, snapshot); return intent; } @@ -116,10 +292,23 @@ export class JsonFileTsnMempool { } async postProof(request) { const snapshot = await readSnapshot(this.path); - if (!snapshot.proofs) - snapshot.proofs = []; snapshot.proofs.push(request); const intent = snapshot.intents.find((candidate) => candidate.id === request.intent_id); + const registryEntry = snapshot.commitmentRegistry.find((candidate) => candidate.transferId === request.intent_id); + if (!registryEntry) + throw new Error("Commitment registry entry not found for proof"); + if (registryEntry.recoverable) + throw new Error("Transfer is already recoverable"); + if (registryEntry.otdtHash && request.otdt_hash && registryEntry.otdtHash !== request.otdt_hash) { + throw new Error("OTDT hash mismatch for settlement proof"); + } + const settlementCommitmentHash = request.settlement_commitment_hash ?? settlementSha256Hex(`${request.intent_id}:${request.proof_tx}:${request.cranker_pubkey}`); + Object.assign(registryEntry, { + settlementCommitmentHash, + settlementProofTx: request.proof_tx, + recoverable: true, + updatedAt: now(), + }); if (intent && ["escrowed", "onchain", "claimed"].includes(intent.status)) { Object.assign(intent, { status: "executed", @@ -127,9 +316,55 @@ export class JsonFileTsnMempool { updatedAt: now(), }); } + const lease = snapshot.claimLeases.find((candidate) => candidate.transferId === request.intent_id && candidate.status === "active"); + if (lease) + Object.assign(lease, { status: "completed", completedAt: now() }); + const claim = snapshot.claimRequests.find((candidate) => candidate.intentId === request.intent_id && candidate.status !== "completed"); + if (claim) + Object.assign(claim, { status: "completed", updatedAt: now() }); + const metrics = refreshLiquidityMetrics(snapshot); + if (!snapshot.recoveryQueue.some((entry) => entry.transferId === request.intent_id)) { + const amount = Number(intent?.amount ?? 0); + const reward = Number((amount * Number(process.env.TSN_RECOVERY_REWARD_BPS ?? 200) / 10_000).toFixed(9)); + const timestamp = now(); + snapshot.recoveryQueue.push({ + id: randomUUID(), + transferId: request.intent_id, + epoch: registryEntry.epoch, + recoverableAmount: amount, + vaultSource: `commitment:${registryEntry.commitmentHash}`, + recoveryReward: reward, + priorityScore: recoveryPriority(metrics, amount), + status: "open", + createdAt: timestamp, + updatedAt: timestamp, + }); + } await writeSnapshot(this.path, snapshot); return request; } + async completeRecoveryJob(jobId, crankerPubkey, proofTx) { + const snapshot = await readSnapshot(this.path); + const job = snapshot.recoveryQueue.find((candidate) => candidate.id === jobId); + if (!job || job.status === "completed") + return job ?? null; + const timestamp = now(); + Object.assign(job, { + status: "completed", + leasedByCrankerPubkey: crankerPubkey, + proofTx, + updatedAt: timestamp, + }); + const registryEntry = snapshot.commitmentRegistry.find((entry) => entry.transferId === job.transferId); + if (registryEntry) + Object.assign(registryEntry, { recoveryProofTx: proofTx, updatedAt: timestamp }); + snapshot.liquidityMetrics ??= defaultLiquidityMetrics(); + snapshot.liquidityMetrics.activeLiquidity += job.recoverableAmount; + snapshot.liquidityMetrics.vaultBalance += job.recoverableAmount; + snapshot.liquidityMetrics.updatedAt = timestamp; + await writeSnapshot(this.path, snapshot); + return job; + } } export class HttpTsnMempool { client; @@ -167,6 +402,40 @@ export class HttpTsnMempool { listPendingIntentWork(limit = 50) { return this.client.listPendingIntentWork(limit); } + listCommitmentRegistry() { + return this.client.get("/commitment-registry"); + } + listClaimPointLedger() { + return this.client.get("/claim-points"); + } + listClaimLeases() { + return this.client.get("/claim-leases"); + } + listRecoveryQueue(params = {}) { + const search = new URLSearchParams(); + if (params.status) + search.set("status", params.status); + const query = search.toString(); + return this.client.get(`/recovery-queue${query ? `?${query}` : ""}`); + } + getLiquidityMetrics() { + return this.client.get("/liquidity-metrics"); + } + submitIntentVerification(id, crankerPubkey, patch = {}) { + return this.client.post(`/intents/${encodeURIComponent(id)}/verify`, { + ...patch, + crankerPubkey, + }); + } + acquireClaimLease(intentId, crankerPubkey) { + return this.client.post(`/intents/${encodeURIComponent(intentId)}/claim-lease`, { crankerPubkey }); + } + completeRecoveryJob(jobId, crankerPubkey, proofTx) { + return this.client.post(`/recovery-queue/${encodeURIComponent(jobId)}/complete`, { + crankerPubkey, + proofTx, + }); + } updateIntentStatus(id, status, patch = {}) { return this.client.updateIntentStatus(id, { ...patch, diff --git a/tsn-sdk/dist/payment-authorization-server.d.ts b/tsn-sdk/dist/payment-authorization-server.d.ts new file mode 100644 index 00000000..0131c02c --- /dev/null +++ b/tsn-sdk/dist/payment-authorization-server.d.ts @@ -0,0 +1,6 @@ +export declare function verifySenderPaymentAuthorization(params: { + senderWallet: string; + signatureBase64: string; + message: string; +}): Promise; +//# sourceMappingURL=payment-authorization-server.d.ts.map \ No newline at end of file diff --git a/tsn-sdk/dist/payment-authorization-server.d.ts.map b/tsn-sdk/dist/payment-authorization-server.d.ts.map new file mode 100644 index 00000000..839ab5ea --- /dev/null +++ b/tsn-sdk/dist/payment-authorization-server.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"payment-authorization-server.d.ts","sourceRoot":"","sources":["../src/payment-authorization-server.ts"],"names":[],"mappings":"AASA,wBAAsB,gCAAgC,CAAC,MAAM,EAAE;IAC7D,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB,oBASA"} \ No newline at end of file diff --git a/tsn-sdk/dist/payment-authorization.d.ts b/tsn-sdk/dist/payment-authorization.d.ts new file mode 100644 index 00000000..978950b8 --- /dev/null +++ b/tsn-sdk/dist/payment-authorization.d.ts @@ -0,0 +1,85 @@ +import { type CreateIntentRequest, type TsnMempoolClaimRequest, type TsnMempoolIntent } from "./contracts.js"; +export declare function createSenderPaymentAuthorizationMessage(params: { + senderWallet: string; + senderIdentity: string; + receiverIdentity: string; + tokenMintAddress: string; + amount: number; + senderFeeAmount: number; + totalTokenRequiredUi: number; + nonce?: string; + issuedAt: string; + expiresAt?: string; +}): string; +export declare function createPaymentAuthorizationNonce(): string; +export declare function createPaymentAuthorizationExpiry(ttlMs?: number): string; +export declare function createPaymentAuthorization(params: { + senderWallet: string; + senderIdentity: string; + receiverIdentity: string; + tokenMintAddress: string; + amount: number; + senderFeeAmount: number; + totalTokenRequiredUi: number; + nonce?: string; + issuedAt?: string; + expiresAt?: string; +}): { + message: string; + nonce: string; + issuedAt: string; + expiresAt: string; +}; +export declare function buildPaymentAuthorizationIntentRequest(params: { + paymentId: string; + recipientHash: string; + tokenMintAddress: string; + senderWallet: string; + senderAuthorizationMessage: string; + senderAuthorizationSignature: string; + senderAuthorizationNonce: string; + senderAuthorizationIssuedAt: string; + senderAuthorizationExpiresAt: string; + senderFeeAmount?: number | null; + senderSignedSettlementTransaction?: string | null; + senderSignedSettlementFeePayer?: string | null; + senderSettlementMode?: "sponsored_sender_cosigned" | string | null; + senderTokenAccount?: string | null; + settlementVault?: string | null; + settlementTokenAccount?: string | null; + settlementPaymentIntentId?: string | null; + amount: number; + recipientAmount?: number; + source?: string; +}): CreateIntentRequest; +export declare function submitPaymentAuthorizationToMempool(params: { + mempoolUrl: string; + fetchImpl?: typeof fetch; + paymentId: string; + recipientHash: string; + tokenMintAddress: string; + senderWallet: string; + senderAuthorizationMessage: string; + senderAuthorizationSignature: string; + senderAuthorizationNonce: string; + senderAuthorizationIssuedAt: string; + senderAuthorizationExpiresAt: string; + senderFeeAmount?: number | null; + senderSignedSettlementTransaction?: string | null; + senderSignedSettlementFeePayer?: string | null; + senderSettlementMode?: "sponsored_sender_cosigned" | string | null; + senderTokenAccount?: string | null; + settlementVault?: string | null; + settlementTokenAccount?: string | null; + settlementPaymentIntentId?: string | null; + amount: number; + recipientAmount?: number; + destinationWallet?: string | null; + autoclaim?: boolean; + source?: string; +}): Promise<{ + intentRequest: CreateIntentRequest; + intent: TsnMempoolIntent; + claimRequest: TsnMempoolClaimRequest | null; +}>; +//# sourceMappingURL=payment-authorization.d.ts.map \ No newline at end of file diff --git a/tsn-sdk/dist/payment-jobs.d.ts b/tsn-sdk/dist/payment-jobs.d.ts new file mode 100644 index 00000000..a97baab9 --- /dev/null +++ b/tsn-sdk/dist/payment-jobs.d.ts @@ -0,0 +1,65 @@ +import { type CreateIntentRequest, type RequestClaimRequest } from "./contracts.js"; +import type { TsnMempool } from "./mempool.js"; +export type TsnSenderBalance = { + balance: number; + symbol?: string | null; +}; +export declare function verifyAuthorizedTsnPaymentRequest(params: { + senderWallet: string; + senderIdentity: string; + receiverIdentity: string; + tokenMintAddress: string; + amount: number; + senderFeeAmount: number; + totalTokenRequiredUi: number; + issuedAt: string; + nonce?: string; + expiresAt?: string; + signatureBase64: string; + maxAgeMs?: number; + getSenderTokenBalance?: (params: { + senderWallet: string; + tokenMintAddress: string; + }) => Promise; +}): Promise<{ + message: string; +}>; +export declare function createTsnPaymentMempoolJobs(params: { + mempool: TsnMempool; + paymentId: string; + underlyingPayment?: string | null; + recipientHash: string; + tokenMintAddress: string; + amount: number; + senderFeeAmount?: number | null; + recipientAmount?: number; + destinationWallet: string; + source?: string; +}): Promise<{ + claimRequestPayload: { + intentId: string; + paymentId: string; + recipientHash: string; + destinationWallet: string; + autoclaim: boolean; + source?: string; + }; + intent: import("./contracts.js").TsnMempoolIntent; + claimRequest: import("./contracts.js").TsnMempoolClaimRequest; + intentRequest: CreateIntentRequest; +}>; +export declare function prepareTsnPaymentMempoolJobRequests(params: { + paymentId: string; + underlyingPayment?: string | null; + recipientHash: string; + tokenMintAddress: string; + amount: number; + senderFeeAmount?: number | null; + recipientAmount?: number; + destinationWallet: string; + source?: string; +}): { + intentRequest: CreateIntentRequest; + claimRequestPayload: RequestClaimRequest; +}; +//# sourceMappingURL=payment-jobs.d.ts.map \ No newline at end of file diff --git a/tsn-sdk/dist/send-estimate.d.ts b/tsn-sdk/dist/send-estimate.d.ts new file mode 100644 index 00000000..7582693f --- /dev/null +++ b/tsn-sdk/dist/send-estimate.d.ts @@ -0,0 +1,26 @@ +export declare function estimateTsnSendCostFromChain(params: { + senderWallet: string; + amountUi: number; + tokenDecimals?: number; + tokenSymbol: string; + tokenUsd?: number | null; + solUsd?: number | null; + rpcUrl?: string; +}): Promise<{ + tokenSymbol: string; + senderFeeAmountUi: number; + senderFeeAmountUsd: number; + totalTokenRequiredUi: number; + networkFeeSol: number; + networkFeeUsd: number; + debug: { + programId: string; + sendFeeBps: number; + feeCoverageTxCount: number; + estimatedNetworkFeeLamports: number; + solUsd: number; + tokenUsd: number; + priceSource: string; + }; +}>; +//# sourceMappingURL=send-estimate.d.ts.map \ No newline at end of file diff --git a/tsn-sdk/dist/send-estimate.d.ts.map b/tsn-sdk/dist/send-estimate.d.ts.map new file mode 100644 index 00000000..27f7c9d0 --- /dev/null +++ b/tsn-sdk/dist/send-estimate.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"send-estimate.d.ts","sourceRoot":"","sources":["../src/send-estimate.ts"],"names":[],"mappings":"AA2FA,wBAAsB,4BAA4B,CAAC,MAAM,EAAE;IACzD,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;;;;;;;;;;;;;;;;GAkDA"} \ No newline at end of file diff --git a/tsn-sdk/dist/send-estimate.js b/tsn-sdk/dist/send-estimate.js new file mode 100644 index 00000000..6ae54eca --- /dev/null +++ b/tsn-sdk/dist/send-estimate.js @@ -0,0 +1,100 @@ +import { Connection, PublicKey, SystemProgram, Transaction, clusterApiUrl, } from "@solana/web3.js"; +import { getVerifiedTsnProgramId } from "./program.js"; +import { quoteTransferFeeUiAmount } from "./quote.js"; +const DEFAULT_TSN_FEE_CONFIG = { + sendFeeBps: 500, + feeCoverageTxCount: 4, + sendFeeMaxUiAmount: 1, + sendFeeMaxUsd: 1, +}; +async function accountDiscriminator(name) { + const payload = new TextEncoder().encode(`account:${name}`); + return crypto.subtle + .digest("SHA-256", payload) + .then((digest) => new Uint8Array(digest).slice(0, 8)); +} +async function fetchEscrowConfigFromChain(connection, programId) { + const [motherEscrowPda] = PublicKey.findProgramAddressSync([new TextEncoder().encode("tsn_mother_escrow")], programId); + const account = await connection.getAccountInfo(motherEscrowPda, "confirmed"); + if (!account?.data) { + throw new Error("TSN mother escrow not found on-chain. Run init-mother for this TSN program."); + } + const data = new Uint8Array(account.data); + const minimumMotherEscrowLength = 8 + 32 + 32 + 32 + 8 + 8 + 2 + 2 + 2 + 8 + 8 + 1; + const discriminator = await accountDiscriminator("MotherEscrow"); + const actual = data.slice(0, 8); + for (let index = 0; index < 8; index += 1) { + if (actual[index] !== discriminator[index]) { + throw new Error("TSN mother escrow discriminator mismatch"); + } + } + if (data.byteLength < minimumMotherEscrowLength) { + return DEFAULT_TSN_FEE_CONFIG; + } + const view = new DataView(data.buffer, data.byteOffset, data.byteLength); + let offset = 8 + 32 + 32 + 32 + 8 + 8; + const feeSplitCrankerBps = view.getUint16(offset, true); + return { + sendFeeBps: Math.max(0, feeSplitCrankerBps), + feeCoverageTxCount: DEFAULT_TSN_FEE_CONFIG.feeCoverageTxCount, + sendFeeMaxUiAmount: DEFAULT_TSN_FEE_CONFIG.sendFeeMaxUiAmount, + sendFeeMaxUsd: DEFAULT_TSN_FEE_CONFIG.sendFeeMaxUsd, + }; +} +async function estimateNetworkFeeLamports(connection, senderWallet) { + const sender = new PublicKey(senderWallet); + const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash("confirmed"); + const transaction = new Transaction({ + feePayer: sender, + blockhash, + lastValidBlockHeight, + }).add(SystemProgram.transfer({ + fromPubkey: sender, + toPubkey: sender, + lamports: 0, + })); + const fee = await connection.getFeeForMessage(transaction.compileMessage(), "confirmed"); + return fee.value ?? 0; +} +export async function estimateTsnSendCostFromChain(params) { + const connection = new Connection(params.rpcUrl ?? clusterApiUrl("devnet"), "confirmed"); + const programId = new PublicKey(getVerifiedTsnProgramId()); + const [config, estimatedNetworkFeeLamports] = await Promise.all([ + fetchEscrowConfigFromChain(connection, programId), + estimateNetworkFeeLamports(connection, params.senderWallet), + ]); + const tokenDecimals = params.tokenDecimals ?? 6; + if (!params.solUsd || !params.tokenUsd) { + throw new Error("Sender fee not fetched: missing price feed"); + } + const senderFeeAmountUi = quoteTransferFeeUiAmount({ + estimatedNetworkFeeLamports, + solUsd: params.solUsd, + tokenUsd: params.tokenUsd, + tokenDecimals, + coverageTxCount: config.feeCoverageTxCount, + feeBps: config.sendFeeBps, + maxMarginUsd: config.sendFeeMaxUsd, + maxUiAmount: config.sendFeeMaxUiAmount, + }); + const networkFeeSol = estimatedNetworkFeeLamports / 1_000_000_000; + const networkFeeUsd = networkFeeSol * params.solUsd; + const senderFeeAmountUsd = senderFeeAmountUi * params.tokenUsd; + return { + tokenSymbol: params.tokenSymbol, + senderFeeAmountUi, + senderFeeAmountUsd, + totalTokenRequiredUi: Number((params.amountUi + senderFeeAmountUi).toFixed(6)), + networkFeeSol, + networkFeeUsd, + debug: { + programId: programId.toBase58(), + sendFeeBps: config.sendFeeBps, + feeCoverageTxCount: config.feeCoverageTxCount, + estimatedNetworkFeeLamports, + solUsd: params.solUsd, + tokenUsd: params.tokenUsd, + priceSource: "tsn-config", + }, + }; +} diff --git a/tsn-sdk/dist/server.js b/tsn-sdk/dist/server.js index 480b2fdf..abdbbdcf 100644 --- a/tsn-sdk/dist/server.js +++ b/tsn-sdk/dist/server.js @@ -7,23 +7,83 @@ async function readJson(request) { for await (const chunk of request) { chunks.push(Buffer.from(chunk)); } - return JSON.parse(Buffer.concat(chunks).toString("utf8")); + const body = Buffer.concat(chunks).toString("utf8"); + return body ? JSON.parse(body) : {}; } function send(response, status, body) { response.writeHead(status, { "Content-Type": "application/json" }); response.end(JSON.stringify(body)); } +function routeUrl(request) { + return new URL(request.url ?? "/", `http://localhost:${port}`); +} createServer(async (request, response) => { try { - if (request.method === "POST" && request.url === "/intents") { + const url = routeUrl(request); + const path = url.pathname; + if (request.method === "POST" && path === "/intents") { return send(response, 200, await mempool.postIntent(await readJson(request))); } - if (request.method === "POST" && request.url === "/claim-requests") { + if (request.method === "GET" && path === "/intents") { + return send(response, 200, await mempool.listIntents({ status: url.searchParams.get("status") })); + } + if (request.method === "POST" && path === "/claim-requests") { return send(response, 200, await mempool.postClaimRequest(await readJson(request))); } - if (request.method === "GET" && request.url?.startsWith("/work")) { - const url = new URL(request.url, `http://localhost:${port}`); - return send(response, 200, { intents: await mempool.listPendingWork(Number(url.searchParams.get("limit") ?? 50)) }); + if (request.method === "GET" && path === "/claim-requests") { + return send(response, 200, await mempool.listClaimRequests({ + intentId: url.searchParams.get("intent_id") ?? undefined, + status: url.searchParams.get("status"), + })); + } + if (request.method === "GET" && path === "/work") { + return send(response, 200, await mempool.listPendingWork(Number(url.searchParams.get("limit") ?? 50))); + } + if (request.method === "GET" && path === "/intent-work") { + return send(response, 200, await mempool.listPendingIntentWork(Number(url.searchParams.get("limit") ?? 50))); + } + if (request.method === "GET" && path === "/commitment-registry") { + return send(response, 200, await mempool.listCommitmentRegistry()); + } + if (request.method === "GET" && path === "/claim-points") { + return send(response, 200, await mempool.listClaimPointLedger()); + } + if (request.method === "GET" && path === "/claim-leases") { + return send(response, 200, await mempool.listClaimLeases()); + } + if (request.method === "GET" && path === "/recovery-queue") { + return send(response, 200, await mempool.listRecoveryQueue({ status: url.searchParams.get("status") })); + } + if (request.method === "GET" && path === "/liquidity-metrics") { + return send(response, 200, await mempool.getLiquidityMetrics()); + } + if (request.method === "POST" && path === "/proofs") { + return send(response, 200, await mempool.postProof(await readJson(request))); + } + const verifyMatch = path.match(/^\/intents\/([^/]+)\/verify$/); + if (request.method === "POST" && verifyMatch) { + const body = await readJson(request); + return send(response, 200, await mempool.submitIntentVerification(decodeURIComponent(verifyMatch[1]), body.crankerPubkey, body)); + } + const leaseMatch = path.match(/^\/intents\/([^/]+)\/claim-lease$/); + if (request.method === "POST" && leaseMatch) { + const body = await readJson(request); + return send(response, 200, await mempool.acquireClaimLease(decodeURIComponent(leaseMatch[1]), body.crankerPubkey)); + } + const intentStatusMatch = path.match(/^\/intents\/([^/]+)\/status$/); + if (request.method === "PATCH" && intentStatusMatch) { + const body = await readJson(request); + return send(response, 200, await mempool.updateIntentStatus(decodeURIComponent(intentStatusMatch[1]), body.status, body)); + } + const claimStatusMatch = path.match(/^\/claim-requests\/([^/]+)\/status$/); + if (request.method === "PATCH" && claimStatusMatch) { + const body = await readJson(request); + return send(response, 200, await mempool.updateClaimRequestStatus(decodeURIComponent(claimStatusMatch[1]), body.status, body)); + } + const recoveryCompleteMatch = path.match(/^\/recovery-queue\/([^/]+)\/complete$/); + if (request.method === "POST" && recoveryCompleteMatch) { + const body = await readJson(request); + return send(response, 200, await mempool.completeRecoveryJob(decodeURIComponent(recoveryCompleteMatch[1]), body.crankerPubkey, body.proofTx)); } return send(response, 404, { error: "Not found" }); } diff --git a/tsn-sdk/dist/settlement-token.d.ts b/tsn-sdk/dist/settlement-token.d.ts new file mode 100644 index 00000000..4cd3b791 --- /dev/null +++ b/tsn-sdk/dist/settlement-token.d.ts @@ -0,0 +1,70 @@ +export declare const TSN_SETTLEMENT_TOKEN_VERSION = 1; +export declare const TSN_SETTLEMENT_TOKEN_ALGORITHM = "TSN-HKDF-SHA256-STREAM-HMAC"; +export type SettlementTokenPlaintext = { + transferId: string; + recipientHash: string; + tokenMintAddress: string; + amount: number; + epoch: number; + nonce: string; + issuedAt: string; +}; +export type EncryptedSettlementToken = { + version: number; + algorithm: typeof TSN_SETTLEMENT_TOKEN_ALGORITHM; + salt: string; + nonce: string; + aad: string; + aadHash: string; + authorizedCrankerDnaHash: string; + ciphertext: string; + tag: string; +}; +export type SettlementTokenBundle = { + plaintext: SettlementTokenPlaintext; + encryptedSettlementToken: string; + commitmentHash: string; +}; +export type OneTimeDecryptionToken = { + id: string; + transferId: string; + leaseId: string; + crankerPubkey: string; + commitmentHash: string; + issuedAt: string; + expiresAt: string; + tokenHash: string; +}; +export declare function settlementSha256Hex(input: string | Buffer | Uint8Array): string; +export declare function canonicalJson(value: unknown): string; +export declare function settlementTokenCommitmentHash(plaintext: SettlementTokenPlaintext): string; +export declare function crankerDnaHash(value: string): string; +export declare function createEncryptedSettlementToken(params: { + transferId: string; + recipientHash: string; + tokenMintAddress: string; + amount: number; + epoch: number; + authorizedCrankerDnaHash?: string | null; + masterKey?: string | Buffer | Uint8Array; + issuedAt?: string; +}): SettlementTokenBundle; +export declare function decodeEncryptedSettlementToken(value: string): EncryptedSettlementToken; +export declare function decryptSettlementToken(params: { + encryptedSettlementToken: string; + transferId: string; + commitmentHash: string; + authorizedCrankerDnaHash?: string | null; + masterKey?: string | Buffer | Uint8Array; +}): SettlementTokenPlaintext; +export declare function createOneTimeDecryptionToken(params: { + transferId: string; + leaseId: string; + crankerPubkey: string; + commitmentHash: string; + ttlMs?: number; + issuedAt?: string; + masterKey?: string | Buffer | Uint8Array; +}): OneTimeDecryptionToken; +export declare function currentTsnEpoch(epochMs?: number): number; +//# sourceMappingURL=settlement-token.d.ts.map \ No newline at end of file diff --git a/tsn-sdk/dist/settlement-token.d.ts.map b/tsn-sdk/dist/settlement-token.d.ts.map new file mode 100644 index 00000000..2d760317 --- /dev/null +++ b/tsn-sdk/dist/settlement-token.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"settlement-token.d.ts","sourceRoot":"","sources":["../src/settlement-token.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/tsn-sdk/dist/settlement-token.js b/tsn-sdk/dist/settlement-token.js new file mode 100644 index 00000000..9617d2ac --- /dev/null +++ b/tsn-sdk/dist/settlement-token.js @@ -0,0 +1,179 @@ +import { createHash, createHmac, randomBytes, timingSafeEqual } from "node:crypto"; +export const TSN_SETTLEMENT_TOKEN_VERSION = 1; +export const TSN_SETTLEMENT_TOKEN_ALGORITHM = "TSN-HKDF-SHA256-STREAM-HMAC"; +function base64UrlEncode(bytes) { + return Buffer.from(bytes) + .toString("base64") + .replace(/=/g, "") + .replace(/\+/g, "-") + .replace(/\//g, "_"); +} +function base64UrlDecode(value) { + const padded = value.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - (value.length % 4)) % 4); + return Buffer.from(padded, "base64"); +} +export function settlementSha256Hex(input) { + return createHash("sha256").update(input).digest("hex"); +} +export function canonicalJson(value) { + if (value == null || typeof value !== "object") + return JSON.stringify(value); + if (Array.isArray(value)) + return `[${value.map((entry) => canonicalJson(entry)).join(",")}]`; + return `{${Object.entries(value) + .sort(([left], [right]) => left.localeCompare(right)) + .map(([key, entry]) => `${JSON.stringify(key)}:${canonicalJson(entry)}`) + .join(",")}}`; +} +export function settlementTokenCommitmentHash(plaintext) { + return settlementSha256Hex(canonicalJson(plaintext)); +} +export function crankerDnaHash(value) { + return settlementSha256Hex(`tsn-cranker-dna:${value}`); +} +function normalizeMasterKey(masterKey) { + const source = masterKey ?? process.env.TSN_SETTLEMENT_TOKEN_MASTER_KEY; + if (!source) { + throw new Error("TSN_SETTLEMENT_TOKEN_MASTER_KEY is required for settlement-token encryption"); + } + if (Buffer.isBuffer(source) || source instanceof Uint8Array) + return Buffer.from(source); + const trimmed = source.trim(); + if (/^[0-9a-f]{64}$/i.test(trimmed)) + return Buffer.from(trimmed, "hex"); + try { + const decoded = Buffer.from(trimmed, "base64"); + if (decoded.length >= 32) + return decoded; + } + catch { + // fall through to hash-based normalization + } + return Buffer.from(settlementSha256Hex(trimmed), "hex"); +} +function hmac(key, value) { + return createHmac("sha256", key).update(value).digest(); +} +function deriveKeys(params) { + const master = normalizeMasterKey(params.masterKey); + const prk = hmac(master, Buffer.from(`tsn-settlement:${params.transferId}:${params.salt}`, "utf8")); + const encKey = hmac(prk, `enc:${params.authorizedCrankerDnaHash}`); + const macKey = hmac(prk, `mac:${params.authorizedCrankerDnaHash}`); + return { encKey, macKey }; +} +function streamXor(input, key, nonce) { + const output = Buffer.alloc(input.length); + let offset = 0; + let counter = 0; + while (offset < input.length) { + const counterBuffer = Buffer.alloc(4); + counterBuffer.writeUInt32BE(counter, 0); + const block = hmac(key, Buffer.concat([nonce, counterBuffer])); + for (let index = 0; index < block.length && offset < input.length; index += 1, offset += 1) { + output[offset] = input[offset] ^ block[index]; + } + counter += 1; + } + return output; +} +export function createEncryptedSettlementToken(params) { + const plaintext = { + transferId: params.transferId, + recipientHash: params.recipientHash, + tokenMintAddress: params.tokenMintAddress, + amount: params.amount, + epoch: params.epoch, + nonce: base64UrlEncode(randomBytes(24)), + issuedAt: params.issuedAt ?? new Date().toISOString(), + }; + const salt = base64UrlEncode(randomBytes(16)); + const nonce = randomBytes(24); + const authorizedCrankerDnaHash = params.authorizedCrankerDnaHash ?? crankerDnaHash(process.env.TSN_CRANKER_DNA ?? "trustlink-authorized-cranker"); + const aad = canonicalJson({ + transferId: params.transferId, + commitmentHash: settlementTokenCommitmentHash(plaintext), + epoch: params.epoch, + authorizedCrankerDnaHash, + }); + const { encKey, macKey } = deriveKeys({ + masterKey: params.masterKey, + transferId: params.transferId, + salt, + authorizedCrankerDnaHash, + }); + const plaintextBytes = Buffer.from(canonicalJson(plaintext), "utf8"); + const ciphertext = streamXor(plaintextBytes, encKey, nonce); + const tag = hmac(macKey, Buffer.concat([Buffer.from(aad, "utf8"), nonce, ciphertext])); + const envelope = { + version: TSN_SETTLEMENT_TOKEN_VERSION, + algorithm: TSN_SETTLEMENT_TOKEN_ALGORITHM, + salt, + nonce: base64UrlEncode(nonce), + aad: base64UrlEncode(Buffer.from(aad, "utf8")), + aadHash: settlementSha256Hex(aad), + authorizedCrankerDnaHash, + ciphertext: base64UrlEncode(ciphertext), + tag: base64UrlEncode(tag), + }; + return { + plaintext, + encryptedSettlementToken: base64UrlEncode(Buffer.from(JSON.stringify(envelope), "utf8")), + commitmentHash: settlementTokenCommitmentHash(plaintext), + }; +} +export function decodeEncryptedSettlementToken(value) { + const parsed = JSON.parse(base64UrlDecode(value).toString("utf8")); + if (parsed.version !== TSN_SETTLEMENT_TOKEN_VERSION || parsed.algorithm !== TSN_SETTLEMENT_TOKEN_ALGORITHM) { + throw new Error("Unsupported TSN settlement token envelope"); + } + return parsed; +} +export function decryptSettlementToken(params) { + const envelope = decodeEncryptedSettlementToken(params.encryptedSettlementToken); + const authorizedCrankerDnaHash = params.authorizedCrankerDnaHash ?? envelope.authorizedCrankerDnaHash; + if (authorizedCrankerDnaHash !== envelope.authorizedCrankerDnaHash) { + throw new Error("Cranker DNA is not authorized for this settlement token"); + } + const { encKey, macKey } = deriveKeys({ + masterKey: params.masterKey, + transferId: params.transferId, + salt: envelope.salt, + authorizedCrankerDnaHash, + }); + const nonce = base64UrlDecode(envelope.nonce); + const ciphertext = base64UrlDecode(envelope.ciphertext); + const aad = base64UrlDecode(envelope.aad).toString("utf8"); + if (settlementSha256Hex(aad) !== envelope.aadHash) { + throw new Error("Settlement token AAD hash mismatch"); + } + const suppliedTag = base64UrlDecode(envelope.tag); + const tag = hmac(macKey, Buffer.concat([Buffer.from(aad, "utf8"), nonce, ciphertext])); + if (suppliedTag.length !== tag.length || !timingSafeEqual(suppliedTag, tag)) { + throw new Error("Settlement token authentication failed"); + } + const plaintext = JSON.parse(streamXor(ciphertext, encKey, nonce).toString("utf8")); + if (plaintext.transferId !== params.transferId) + throw new Error("Settlement token transfer ID mismatch"); + if (settlementTokenCommitmentHash(plaintext) !== params.commitmentHash) { + throw new Error("Settlement token commitment mismatch"); + } + return plaintext; +} +export function createOneTimeDecryptionToken(params) { + const issuedAt = params.issuedAt ?? new Date().toISOString(); + const expiresAt = new Date(Date.parse(issuedAt) + (params.ttlMs ?? 10 * 60_000)).toISOString(); + const secret = base64UrlEncode(randomBytes(32)); + return { + id: base64UrlEncode(randomBytes(16)), + transferId: params.transferId, + leaseId: params.leaseId, + crankerPubkey: params.crankerPubkey, + commitmentHash: params.commitmentHash, + issuedAt, + expiresAt, + tokenHash: settlementSha256Hex(`${params.transferId}:${params.leaseId}:${params.crankerPubkey}:${params.commitmentHash}:${secret}`), + }; +} +export function currentTsnEpoch(epochMs = 60 * 60 * 1000) { + return Math.floor(Date.now() / epochMs); +} diff --git a/tsn-sdk/dist/sponsored-settlement.d.ts b/tsn-sdk/dist/sponsored-settlement.d.ts new file mode 100644 index 00000000..262a7fd4 --- /dev/null +++ b/tsn-sdk/dist/sponsored-settlement.d.ts @@ -0,0 +1,50 @@ +import { PublicKey } from "@solana/web3.js"; +export declare function uiAmountToBaseUnits(amountUi: number | string, decimals: number): bigint; +export declare function tsnInstructionDiscriminator(name: string): Uint8Array; +export declare function paymentIdToU64(paymentId: string): bigint; +export declare function getSponsoredSettlementPdas(params: { + paymentId: string; + crankerFeePayer: string | PublicKey; + tokenMintAddress: string | PublicKey; + intentSeedHash?: string; +}): { + programId: PublicKey; + crankerOperator: PublicKey; + mint: PublicKey; + intentSeed32: Uint8Array; + intentSeedHash: string; + paymentIntentId: bigint; + verifierPda: PublicKey; + treasuryPda: PublicKey; + treasuryTokenAccount: PublicKey; + paymentVault: PublicKey; + paymentVaultTokenAccount: PublicKey; +}; +export declare function buildTsnSponsoredSettlementTransaction(params: { + paymentId: string; + crankerFeePayer: string; + senderWallet: string; + tokenMintAddress: string; + amountUi: number | string; + senderFeeAmountUi?: number | string; + tokenDecimals: number; + recipientHash: string; + rpcUrl?: string; + intentSeedHash?: string; +}): Promise<{ + transactionBase64: string; + intentSeedHash: string; + paymentIntentId: string; + paymentVault: string; + paymentVaultTokenAccount: string; + senderTokenAccount: string; + crankerFeePayer: string; + verifierPda: string; + treasuryPda: string; + treasuryTokenAccount: string; + amountBaseUnits: string; + senderFeeAmountBaseUnits: string; + blockhash: string; + lastValidBlockHeight: number; +}>; +//# sourceMappingURL=sponsored-settlement.d.ts.map \ No newline at end of file diff --git a/tsn-sdk/dist/sponsored-settlement.js b/tsn-sdk/dist/sponsored-settlement.js new file mode 100644 index 00000000..e86fb3ac --- /dev/null +++ b/tsn-sdk/dist/sponsored-settlement.js @@ -0,0 +1,173 @@ +import { Connection, PublicKey, SystemProgram, Transaction, TransactionInstruction, clusterApiUrl, } from "@solana/web3.js"; +import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, createTransferCheckedInstruction, getAssociatedTokenAddressSync, } from "@solana/spl-token"; +import { sha256 } from "@noble/hashes/sha2"; +import { bytesToHex, utf8ToBytes } from "@noble/hashes/utils"; +import { Buffer } from "buffer"; +import { VERIFIED_TSN_PROGRAM_ID } from "./program.js"; +const TSN_VERIFIER_SEED = utf8ToBytes("verifier"); +const TSN_TREASURY_SEED = utf8ToBytes("tsn_treasury"); +const TSN_PAYMENT_VAULT_SEED = utf8ToBytes("vault"); +function concatBytes(parts) { + const length = parts.reduce((total, part) => total + part.length, 0); + const output = new Uint8Array(length); + let offset = 0; + for (const part of parts) { + output.set(part, offset); + offset += part.length; + } + return output; +} +function encodeU64(value) { + if (value < 0n || value > 0xffffffffffffffffn) { + throw new Error("u64 value is out of range"); + } + const output = new Uint8Array(8); + let remaining = value; + for (let index = 0; index < output.length; index += 1) { + output[index] = Number(remaining & 0xffn); + remaining >>= 8n; + } + return output; +} +function bytesToBase64(bytes) { + if (typeof btoa === "function") { + let binary = ""; + const chunkSize = 0x8000; + for (let offset = 0; offset < bytes.length; offset += chunkSize) { + binary += String.fromCharCode(...bytes.subarray(offset, offset + chunkSize)); + } + return btoa(binary); + } + return Buffer.from(bytes).toString("base64"); +} +function hexToBytes(value, label) { + const normalized = value.trim(); + if (!/^[0-9a-fA-F]+$/.test(normalized) || normalized.length % 2 !== 0) { + throw new Error(`${label} must be a hex string`); + } + const output = new Uint8Array(normalized.length / 2); + for (let index = 0; index < output.length; index += 1) { + output[index] = Number.parseInt(normalized.slice(index * 2, index * 2 + 2), 16); + } + return output; +} +export function uiAmountToBaseUnits(amountUi, decimals) { + if (!Number.isInteger(decimals) || decimals < 0) { + throw new Error("token decimals must be a non-negative integer"); + } + const raw = String(amountUi).trim(); + if (!/^\d+(\.\d+)?$/.test(raw)) { + throw new Error(`Invalid token amount: ${raw}`); + } + const [whole, fraction = ""] = raw.split("."); + const normalizedFraction = fraction.padEnd(decimals, "0").slice(0, decimals); + return BigInt(whole || "0") * 10n ** BigInt(decimals) + BigInt(normalizedFraction || "0"); +} +export function tsnInstructionDiscriminator(name) { + return sha256(utf8ToBytes(`global:${name}`)).subarray(0, 8); +} +export function paymentIdToU64(paymentId) { + const hash = sha256(utf8ToBytes(paymentId)); + let value = 0n; + for (let index = 0; index < 8; index += 1) { + value |= BigInt(hash[index]) << (8n * BigInt(index)); + } + return value; +} +export function getSponsoredSettlementPdas(params) { + const programId = new PublicKey(VERIFIED_TSN_PROGRAM_ID); + const crankerOperator = params.crankerFeePayer instanceof PublicKey + ? params.crankerFeePayer + : new PublicKey(params.crankerFeePayer); + const mint = params.tokenMintAddress instanceof PublicKey + ? params.tokenMintAddress + : new PublicKey(params.tokenMintAddress); + const intentSeed32 = params.intentSeedHash + ? hexToBytes(params.intentSeedHash, "intentSeedHash") + : sha256(utf8ToBytes(params.paymentId)); + if (intentSeed32.length !== 32) { + throw new Error("intentSeedHash must decode to 32 bytes"); + } + const paymentIntentId = paymentIdToU64(params.paymentId); + const verifierPda = PublicKey.findProgramAddressSync([TSN_VERIFIER_SEED], programId)[0]; + const treasuryPda = PublicKey.findProgramAddressSync([TSN_TREASURY_SEED], programId)[0]; + const treasuryTokenAccount = getAssociatedTokenAddressSync(mint, treasuryPda, true); + const paymentVault = PublicKey.findProgramAddressSync([TSN_PAYMENT_VAULT_SEED, encodeU64(paymentIntentId)], programId)[0]; + const paymentVaultTokenAccount = getAssociatedTokenAddressSync(mint, paymentVault, true); + return { + programId, + crankerOperator, + mint, + intentSeed32, + intentSeedHash: bytesToHex(intentSeed32), + paymentIntentId, + verifierPda, + treasuryPda, + treasuryTokenAccount, + paymentVault, + paymentVaultTokenAccount, + }; +} +export async function buildTsnSponsoredSettlementTransaction(params) { + const connection = new Connection(params.rpcUrl ?? clusterApiUrl("devnet"), "confirmed"); + const senderWallet = new PublicKey(params.senderWallet); + if (hexToBytes(params.recipientHash, "recipientHash").length !== 32) { + throw new Error("recipientHash must be a 32-byte hex string"); + } + const pdas = getSponsoredSettlementPdas({ + paymentId: params.paymentId, + crankerFeePayer: params.crankerFeePayer, + tokenMintAddress: params.tokenMintAddress, + intentSeedHash: params.intentSeedHash, + }); + const senderTokenAccount = getAssociatedTokenAddressSync(pdas.mint, senderWallet); + const amountBaseUnits = uiAmountToBaseUnits(params.amountUi, params.tokenDecimals); + const senderFeeAmountBaseUnits = uiAmountToBaseUnits(params.senderFeeAmountUi ?? 0, params.tokenDecimals); + const latestBlockhash = await connection.getLatestBlockhash("confirmed"); + const processPaymentIntentIx = new TransactionInstruction({ + programId: pdas.programId, + keys: [ + { pubkey: pdas.crankerOperator, isSigner: true, isWritable: true }, + { pubkey: pdas.verifierPda, isSigner: false, isWritable: true }, + { pubkey: pdas.paymentVault, isSigner: false, isWritable: true }, + { pubkey: pdas.paymentVaultTokenAccount, isSigner: false, isWritable: true }, + { pubkey: pdas.mint, isSigner: false, isWritable: false }, + { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, + { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, + { pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, + ], + data: Buffer.from(concatBytes([ + tsnInstructionDiscriminator("tsn_process_payment_intent"), + encodeU64(pdas.paymentIntentId), + encodeU64(amountBaseUnits), + ])), + }); + const lockFundsIx = createTransferCheckedInstruction(senderTokenAccount, pdas.mint, pdas.paymentVaultTokenAccount, senderWallet, amountBaseUnits, params.tokenDecimals); + const transferSenderFeeIx = senderFeeAmountBaseUnits > 0n + ? createTransferCheckedInstruction(senderTokenAccount, pdas.mint, pdas.treasuryTokenAccount, senderWallet, senderFeeAmountBaseUnits, params.tokenDecimals) + : null; + const transaction = new Transaction({ + feePayer: pdas.crankerOperator, + blockhash: latestBlockhash.blockhash, + lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, + }).add(processPaymentIntentIx, lockFundsIx, ...(transferSenderFeeIx ? [transferSenderFeeIx] : [])); + return { + transactionBase64: bytesToBase64(transaction.serialize({ + requireAllSignatures: false, + verifySignatures: false, + })), + intentSeedHash: pdas.intentSeedHash, + paymentIntentId: pdas.paymentIntentId.toString(), + paymentVault: pdas.paymentVault.toBase58(), + paymentVaultTokenAccount: pdas.paymentVaultTokenAccount.toBase58(), + senderTokenAccount: senderTokenAccount.toBase58(), + crankerFeePayer: pdas.crankerOperator.toBase58(), + verifierPda: pdas.verifierPda.toBase58(), + treasuryPda: pdas.treasuryPda.toBase58(), + treasuryTokenAccount: pdas.treasuryTokenAccount.toBase58(), + amountBaseUnits: amountBaseUnits.toString(), + senderFeeAmountBaseUnits: senderFeeAmountBaseUnits.toString(), + blockhash: latestBlockhash.blockhash, + lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, + }; +} diff --git a/tsn-sdk/dist/tins.d.ts b/tsn-sdk/dist/tins.d.ts new file mode 100644 index 00000000..517e8069 --- /dev/null +++ b/tsn-sdk/dist/tins.d.ts @@ -0,0 +1,192 @@ +import { Buffer } from "buffer"; +import { Connection, PublicKey, TransactionInstruction } from "@solana/web3.js"; +export declare const DEFAULT_TINS_PROGRAM_ID = "TinseNnU588NkmRZBe4ADJbxqrqQma92678UFP6VuwT"; +export declare const TINS_PROGRAM_SALT = "TINS_SALT_2026"; +export type TinSocialIdentityType = "whatsapp" | "x" | "email" | "telegram" | "discord" | string; +export type TinEncryptedSocialIdentity = { + identityType: TinSocialIdentityType; + label: string; + nonce: Uint8Array; + ciphertext: Uint8Array; + metadata: string; + verifiedBy: PublicKey | null; + proofHash: Uint8Array; + linkedAt: bigint; +}; +export type TinEncryptedSensitiveField = { + fieldType: "kyc_document_hash" | string; + nonce: Uint8Array; + ciphertext: Uint8Array; + metadata: string; + proofHash: Uint8Array; + linkedAt: bigint; +}; +export type TinIdentityRegistry = { + version: number; + bump: number; + status: number; + tin: bigint; + authority: PublicKey; + masterPrivacy: PublicKey; + lastEscrowId: bigint; + createdAt: bigint; + name: string; + socialIdentities: TinEncryptedSocialIdentity[]; + sensitiveFields: TinEncryptedSensitiveField[]; +}; +export type TinResolvedIdentity = { + tin: string; + name: string; + authority: PublicKey; + socialIdentities: Array<{ + type: TinSocialIdentityType; + label: string; + value: string; + metadata: unknown; + verifiedBy: string | null; + linkedAt: string; + }>; + sensitiveFields: Array<{ + type: string; + value: string; + metadata: unknown; + linkedAt: string; + }>; + encryptedSensitiveFields: TinEncryptedSensitiveField[]; +}; +export declare function getTinsIdentitySeed(walletPubkey: PublicKey): Buffer; +export declare function getTinsGlobalStatePda(programId?: PublicKey | string | null): PublicKey; +export declare function getTinsIdentityPda(params: { + walletPubkey: PublicKey; + programId?: PublicKey | string | null; +}): PublicKey; +export declare function getTinsRegistryPda(params: { + tin: bigint | number | string; + programId?: PublicKey | string | null; +}): PublicKey; +export declare function getTinsPlatformRegistryPda(programId?: PublicKey | string | null): PublicKey; +export declare function buildCreateTinInstruction(params: { + payer: PublicKey; + identity: PublicKey; + displayName: string; + encryptedPhone: Uint8Array; + programId?: PublicKey | string | null; +}): TransactionInstruction; +export declare function buildInitializePlatformRegistryInstruction(params: { + authority: PublicKey; + platformRegistry?: PublicKey; + programId?: PublicKey | string | null; +}): TransactionInstruction; +export declare function buildUpsertVerificationPlatformInstruction(params: { + authority: PublicKey; + platformId: string; + platformPubkey: PublicKey; + rotatedFrom?: PublicKey | null; + platformRegistry?: PublicKey; + programId?: PublicKey | string | null; +}): TransactionInstruction; +export declare function buildRemoveVerificationPlatformInstruction(params: { + authority: PublicKey; + platformPubkey: PublicKey; + platformRegistry?: PublicKey; + programId?: PublicKey | string | null; +}): TransactionInstruction; +export declare function buildLinkSocialIdentityInstruction(params: { + owner: PublicKey; + registry: PublicKey; + identityType: TinSocialIdentityType; + label?: string; + nonce: Uint8Array; + ciphertext: Uint8Array; + metadata?: string; + programId?: PublicKey | string | null; +}): TransactionInstruction; +export declare function buildLinkSensitiveFieldInstruction(params: { + owner: PublicKey; + registry: PublicKey; + fieldType: string; + nonce: Uint8Array; + ciphertext: Uint8Array; + metadata?: string; + userAuthorizationHash: Uint8Array; + programId?: PublicKey | string | null; +}): TransactionInstruction; +export declare function buildPlatformSignedProofMessage(params: { + tin: bigint | number | string; + identityType: string; + label?: string; + encryptedPayloadHash: Uint8Array; + subjectWallet: PublicKey; + issuedAt: bigint | number; +}): Buffer; +export declare function buildLinkVerifiedSocialIdentityInstructions(params: { + owner: PublicKey; + registry: PublicKey; + platformPubkey: PublicKey; + platformSignature: Uint8Array; + proofMessage: Uint8Array; + identityType: TinSocialIdentityType; + label?: string; + nonce: Uint8Array; + ciphertext: Uint8Array; + metadata?: string; + platformRegistry?: PublicKey; + programId?: PublicKey | string | null; +}): TransactionInstruction[]; +export declare function decodeTinAccount(data: Uint8Array): { + tin: bigint; + displayName: string; + identityPubkey: PublicKey; + encryptedPhone: Buffer; + createdAt: bigint; +}; +export declare function decodeTinsIdentityRegistry(data: Uint8Array): TinIdentityRegistry; +export declare function deriveTinSocialKey(tin: bigint | number | string): Uint8Array; +export declare function buildSensitiveAuthorizationMessage(params: { + tin: bigint | number | string; + fieldType: string; + nonce?: string; +}): string; +export declare function deriveTinSensitiveKey(params: { + tin: bigint | number | string; + userSignature: Uint8Array | string; + fieldType: string; +}): Uint8Array; +export declare function encryptTinSocialIdentity(params: { + tin: bigint | number | string; + value: string; + nonce?: Uint8Array; +}): Promise<{ + nonce: Uint8Array; + ciphertext: Uint8Array; +}>; +export declare function decryptTinSocialIdentity(params: { + tin: bigint | number | string; + nonce: Uint8Array; + ciphertext: Uint8Array; +}): Promise; +export declare function encryptTinSensitiveField(params: { + tin: bigint | number | string; + fieldType: string; + value: string; + userSignature: Uint8Array | string; + nonce?: Uint8Array; +}): Promise<{ + nonce: Uint8Array; + ciphertext: Uint8Array; + userAuthorizationHash: Uint8Array; +}>; +export declare function decryptTinSensitiveField(params: { + tin: bigint | number | string; + fieldType: string; + nonce: Uint8Array; + ciphertext: Uint8Array; + userSignature: Uint8Array | string; +}): Promise; +export declare function resolveTIN(params: { + tin: bigint | number | string; + connection: Connection; + programId?: PublicKey | string | null; + sensitiveAuthorizations?: Record; +}): Promise; +//# sourceMappingURL=tins.d.ts.map \ No newline at end of file diff --git a/tsn-sdk/dist/tins.d.ts.map b/tsn-sdk/dist/tins.d.ts.map new file mode 100644 index 00000000..19b84714 --- /dev/null +++ b/tsn-sdk/dist/tins.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"tins.d.ts","sourceRoot":"","sources":["../src/tins.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/tsn-sdk/dist/tins.js b/tsn-sdk/dist/tins.js new file mode 100644 index 00000000..4de20489 --- /dev/null +++ b/tsn-sdk/dist/tins.js @@ -0,0 +1,422 @@ +import { Buffer } from "buffer"; +import { sha256 } from "@noble/hashes/sha2"; +import { Ed25519Program, PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY, SystemProgram, TransactionInstruction, } from "@solana/web3.js"; +export const DEFAULT_TINS_PROGRAM_ID = "TinseNnU588NkmRZBe4ADJbxqrqQma92678UFP6VuwT"; +export const TINS_PROGRAM_SALT = "TINS_SALT_2026"; +const TINS_GLOBAL_STATE_SEED = Buffer.from("global-state"); +const TINS_IDENTITY_SEED = Buffer.from("identity"); +const TINS_REGISTRY_SEED = Buffer.from("registry"); +const TINS_PLATFORM_REGISTRY_SEED = Buffer.from("platform-registry"); +const TEXT_ENCODER = new TextEncoder(); +const TEXT_DECODER = new TextDecoder(); +function getTinsProgramPublicKey(programId) { + return programId instanceof PublicKey ? programId : new PublicKey(programId ?? DEFAULT_TINS_PROGRAM_ID); +} +export function getTinsIdentitySeed(walletPubkey) { + return Buffer.from(sha256(Buffer.concat([walletPubkey.toBuffer(), Buffer.from(TINS_PROGRAM_SALT, "utf8")]))); +} +export function getTinsGlobalStatePda(programId) { + return PublicKey.findProgramAddressSync([TINS_GLOBAL_STATE_SEED], getTinsProgramPublicKey(programId))[0]; +} +export function getTinsIdentityPda(params) { + return PublicKey.findProgramAddressSync([TINS_IDENTITY_SEED, getTinsIdentitySeed(params.walletPubkey)], getTinsProgramPublicKey(params.programId))[0]; +} +export function getTinsRegistryPda(params) { + const tinBuffer = Buffer.alloc(8); + tinBuffer.writeBigUInt64LE(BigInt(params.tin)); + return PublicKey.findProgramAddressSync([TINS_REGISTRY_SEED, tinBuffer], getTinsProgramPublicKey(params.programId))[0]; +} +export function getTinsPlatformRegistryPda(programId) { + return PublicKey.findProgramAddressSync([TINS_PLATFORM_REGISTRY_SEED], getTinsProgramPublicKey(programId))[0]; +} +function appendU8(parts, value) { + const buffer = Buffer.alloc(1); + buffer.writeUInt8(value); + parts.push(buffer); +} +function appendU32(parts, value) { + const buffer = Buffer.alloc(4); + buffer.writeUInt32LE(value); + parts.push(buffer); +} +function appendString(parts, value) { + const buffer = Buffer.from(value, "utf8"); + appendU32(parts, buffer.length); + parts.push(buffer); +} +function appendBytes(parts, value) { + const buffer = Buffer.from(value); + appendU32(parts, buffer.length); + parts.push(buffer); +} +function appendPubkey(parts, value) { + parts.push(value.toBuffer()); +} +function appendOptionPubkey(parts, value) { + appendU8(parts, value ? 1 : 0); + if (value) + appendPubkey(parts, value); +} +function encodeInstruction(tag, parts) { + return Buffer.concat([Buffer.from([tag]), ...parts]); +} +export function buildCreateTinInstruction(params) { + const name = Buffer.from(params.displayName, "utf8"); + const encryptedPhone = Buffer.from(params.encryptedPhone); + const data = Buffer.alloc(1 + 4 + name.length + 4 + encryptedPhone.length); + let offset = 0; + data.writeUInt8(4, offset); + offset += 1; + data.writeUInt32LE(name.length, offset); + offset += 4; + name.copy(data, offset); + offset += name.length; + data.writeUInt32LE(encryptedPhone.length, offset); + offset += 4; + encryptedPhone.copy(data, offset); + const program = getTinsProgramPublicKey(params.programId); + return new TransactionInstruction({ + programId: program, + keys: [ + { pubkey: params.payer, isSigner: true, isWritable: true }, + { pubkey: getTinsGlobalStatePda(program), isSigner: false, isWritable: true }, + { pubkey: params.identity, isSigner: false, isWritable: true }, + { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, + ], + data, + }); +} +export function buildInitializePlatformRegistryInstruction(params) { + const program = getTinsProgramPublicKey(params.programId); + return new TransactionInstruction({ + programId: program, + keys: [ + { pubkey: params.authority, isSigner: true, isWritable: true }, + { pubkey: params.platformRegistry ?? getTinsPlatformRegistryPda(program), isSigner: false, isWritable: true }, + { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, + ], + data: encodeInstruction(6, []), + }); +} +export function buildUpsertVerificationPlatformInstruction(params) { + const parts = []; + appendString(parts, params.platformId); + appendPubkey(parts, params.platformPubkey); + appendOptionPubkey(parts, params.rotatedFrom ?? null); + const program = getTinsProgramPublicKey(params.programId); + return new TransactionInstruction({ + programId: program, + keys: [ + { pubkey: params.authority, isSigner: true, isWritable: true }, + { pubkey: params.platformRegistry ?? getTinsPlatformRegistryPda(program), isSigner: false, isWritable: true }, + { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, + ], + data: encodeInstruction(7, parts), + }); +} +export function buildRemoveVerificationPlatformInstruction(params) { + const parts = []; + appendPubkey(parts, params.platformPubkey); + const program = getTinsProgramPublicKey(params.programId); + return new TransactionInstruction({ + programId: program, + keys: [ + { pubkey: params.authority, isSigner: true, isWritable: true }, + { pubkey: params.platformRegistry ?? getTinsPlatformRegistryPda(program), isSigner: false, isWritable: true }, + ], + data: encodeInstruction(8, parts), + }); +} +export function buildLinkSocialIdentityInstruction(params) { + const parts = []; + appendString(parts, params.identityType); + appendString(parts, params.label ?? ""); + appendBytes(parts, params.nonce); + appendBytes(parts, params.ciphertext); + appendString(parts, params.metadata ?? "{}"); + return new TransactionInstruction({ + programId: getTinsProgramPublicKey(params.programId), + keys: [ + { pubkey: params.owner, isSigner: true, isWritable: true }, + { pubkey: params.registry, isSigner: false, isWritable: true }, + { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, + ], + data: encodeInstruction(9, parts), + }); +} +export function buildLinkSensitiveFieldInstruction(params) { + if (params.userAuthorizationHash.length !== 32) + throw new Error("userAuthorizationHash must be 32 bytes"); + const parts = []; + appendString(parts, params.fieldType); + appendBytes(parts, params.nonce); + appendBytes(parts, params.ciphertext); + appendString(parts, params.metadata ?? "{}"); + parts.push(Buffer.from(params.userAuthorizationHash)); + return new TransactionInstruction({ + programId: getTinsProgramPublicKey(params.programId), + keys: [ + { pubkey: params.owner, isSigner: true, isWritable: true }, + { pubkey: params.registry, isSigner: false, isWritable: true }, + { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, + ], + data: encodeInstruction(10, parts), + }); +} +export function buildPlatformSignedProofMessage(params) { + if (params.encryptedPayloadHash.length !== 32) + throw new Error("encryptedPayloadHash must be 32 bytes"); + return Buffer.concat([ + Buffer.from("TINS_PLATFORM_PROOF_V1", "utf8"), + Buffer.from(String(params.tin), "utf8"), + Buffer.from(params.identityType, "utf8"), + Buffer.from(params.label ?? "", "utf8"), + Buffer.from(params.encryptedPayloadHash), + params.subjectWallet.toBuffer(), + Buffer.from(BigInt(params.issuedAt).toString(), "utf8"), + ]); +} +export function buildLinkVerifiedSocialIdentityInstructions(params) { + const parts = []; + appendString(parts, params.identityType); + appendString(parts, params.label ?? ""); + appendBytes(parts, params.nonce); + appendBytes(parts, params.ciphertext); + appendString(parts, params.metadata ?? "{}"); + appendPubkey(parts, params.platformPubkey); + appendBytes(parts, params.proofMessage); + const program = getTinsProgramPublicKey(params.programId); + return [ + Ed25519Program.createInstructionWithPublicKey({ + publicKey: params.platformPubkey.toBytes(), + message: params.proofMessage, + signature: params.platformSignature, + }), + new TransactionInstruction({ + programId: program, + keys: [ + { pubkey: params.owner, isSigner: true, isWritable: true }, + { pubkey: params.registry, isSigner: false, isWritable: true }, + { pubkey: params.platformRegistry ?? getTinsPlatformRegistryPda(program), isSigner: false, isWritable: false }, + { pubkey: SYSVAR_INSTRUCTIONS_PUBKEY, isSigner: false, isWritable: false }, + { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, + ], + data: encodeInstruction(11, parts), + }), + ]; +} +export function decodeTinAccount(data) { + const buffer = Buffer.from(data); + let offset = 0; + const tin = buffer.readBigUInt64LE(offset); + offset += 8; + const displayNameLength = buffer.readUInt32LE(offset); + offset += 4; + const displayName = buffer.subarray(offset, offset + displayNameLength).toString("utf8"); + offset += displayNameLength; + const identityPubkey = new PublicKey(buffer.subarray(offset, offset + 32)); + offset += 32; + const encryptedPhoneLength = buffer.readUInt32LE(offset); + offset += 4; + const encryptedPhone = buffer.subarray(offset, offset + encryptedPhoneLength); + offset += encryptedPhoneLength; + const createdAt = buffer.readBigInt64LE(offset); + return { + tin, + displayName, + identityPubkey, + encryptedPhone, + createdAt, + }; +} +function readString(buffer, offsetRef) { + const length = buffer.readUInt32LE(offsetRef.offset); + offsetRef.offset += 4; + const value = buffer.subarray(offsetRef.offset, offsetRef.offset + length).toString("utf8"); + offsetRef.offset += length; + return value; +} +function readBytes(buffer, offsetRef) { + const length = buffer.readUInt32LE(offsetRef.offset); + offsetRef.offset += 4; + const value = buffer.subarray(offsetRef.offset, offsetRef.offset + length); + offsetRef.offset += length; + return value; +} +function readPubkey(buffer, offsetRef) { + const value = new PublicKey(buffer.subarray(offsetRef.offset, offsetRef.offset + 32)); + offsetRef.offset += 32; + return value; +} +function readOptionPubkey(buffer, offsetRef) { + const tag = buffer.readUInt8(offsetRef.offset); + offsetRef.offset += 1; + return tag === 1 ? readPubkey(buffer, offsetRef) : null; +} +export function decodeTinsIdentityRegistry(data) { + const buffer = Buffer.from(data); + const offsetRef = { offset: 0 }; + const version = buffer.readUInt8(offsetRef.offset++); + const bump = buffer.readUInt8(offsetRef.offset++); + const status = buffer.readUInt8(offsetRef.offset++); + offsetRef.offset += 5; + const tin = buffer.readBigUInt64LE(offsetRef.offset); + offsetRef.offset += 8; + const authority = readPubkey(buffer, offsetRef); + const masterPrivacy = readPubkey(buffer, offsetRef); + const lastEscrowId = buffer.readBigUInt64LE(offsetRef.offset); + offsetRef.offset += 8; + const createdAt = buffer.readBigInt64LE(offsetRef.offset); + offsetRef.offset += 8; + const name = readString(buffer, offsetRef); + const socialCount = buffer.readUInt32LE(offsetRef.offset); + offsetRef.offset += 4; + const socialIdentities = []; + for (let index = 0; index < socialCount; index += 1) { + socialIdentities.push({ + identityType: readString(buffer, offsetRef), + label: readString(buffer, offsetRef), + nonce: readBytes(buffer, offsetRef), + ciphertext: readBytes(buffer, offsetRef), + metadata: readString(buffer, offsetRef), + verifiedBy: readOptionPubkey(buffer, offsetRef), + proofHash: buffer.subarray(offsetRef.offset, offsetRef.offset + 32), + linkedAt: buffer.readBigInt64LE(offsetRef.offset + 32), + }); + offsetRef.offset += 40; + } + const sensitiveCount = buffer.readUInt32LE(offsetRef.offset); + offsetRef.offset += 4; + const sensitiveFields = []; + for (let index = 0; index < sensitiveCount; index += 1) { + sensitiveFields.push({ + fieldType: readString(buffer, offsetRef), + nonce: readBytes(buffer, offsetRef), + ciphertext: readBytes(buffer, offsetRef), + metadata: readString(buffer, offsetRef), + proofHash: buffer.subarray(offsetRef.offset, offsetRef.offset + 32), + linkedAt: buffer.readBigInt64LE(offsetRef.offset + 32), + }); + offsetRef.offset += 40; + } + return { + version, + bump, + status, + tin, + authority, + masterPrivacy, + lastEscrowId, + createdAt, + name, + socialIdentities, + sensitiveFields, + }; +} +function parseMetadata(value) { + try { + return JSON.parse(value); + } + catch { + return value; + } +} +function getWebCrypto() { + const cryptoApi = globalThis.crypto; + if (!cryptoApi?.subtle) + throw new Error("WebCrypto subtle API is required for TIN encryption"); + return cryptoApi; +} +function randomNonce(length = 12) { + const nonce = new Uint8Array(length); + getWebCrypto().getRandomValues(nonce); + return nonce; +} +async function importAesKey(keyMaterial) { + return getWebCrypto().subtle.importKey("raw", keyMaterial, "AES-GCM", false, ["encrypt", "decrypt"]); +} +export function deriveTinSocialKey(tin) { + return sha256(TEXT_ENCODER.encode(`trustlink:tins:social:v1:${String(tin)}`)); +} +export function buildSensitiveAuthorizationMessage(params) { + return `TrustLink TINS sensitive decrypt\nTIN: ${String(params.tin)}\nField: ${params.fieldType}\nNonce: ${params.nonce ?? ""}`; +} +export function deriveTinSensitiveKey(params) { + const signature = typeof params.userSignature === "string" + ? TEXT_ENCODER.encode(params.userSignature) + : params.userSignature; + return sha256(Buffer.concat([ + TEXT_ENCODER.encode(`trustlink:tins:sensitive:v1:${String(params.tin)}:${params.fieldType}:`), + Buffer.from(signature), + ])); +} +export async function encryptTinSocialIdentity(params) { + const nonce = params.nonce ?? randomNonce(); + const key = await importAesKey(deriveTinSocialKey(params.tin)); + const ciphertext = new Uint8Array(await getWebCrypto().subtle.encrypt({ name: "AES-GCM", iv: nonce }, key, TEXT_ENCODER.encode(params.value))); + return { nonce, ciphertext }; +} +export async function decryptTinSocialIdentity(params) { + const key = await importAesKey(deriveTinSocialKey(params.tin)); + const plaintext = await getWebCrypto().subtle.decrypt({ name: "AES-GCM", iv: params.nonce }, key, params.ciphertext); + return TEXT_DECODER.decode(plaintext); +} +export async function encryptTinSensitiveField(params) { + const nonce = params.nonce ?? randomNonce(); + const key = await importAesKey(deriveTinSensitiveKey(params)); + const ciphertext = new Uint8Array(await getWebCrypto().subtle.encrypt({ name: "AES-GCM", iv: nonce }, key, TEXT_ENCODER.encode(params.value))); + const signatureBytes = typeof params.userSignature === "string" + ? TEXT_ENCODER.encode(params.userSignature) + : params.userSignature; + return { nonce, ciphertext, userAuthorizationHash: sha256(signatureBytes) }; +} +export async function decryptTinSensitiveField(params) { + const key = await importAesKey(deriveTinSensitiveKey(params)); + const plaintext = await getWebCrypto().subtle.decrypt({ name: "AES-GCM", iv: params.nonce }, key, params.ciphertext); + return TEXT_DECODER.decode(plaintext); +} +export async function resolveTIN(params) { + const registryPda = getTinsRegistryPda({ tin: params.tin, programId: params.programId }); + const account = await params.connection.getAccountInfo(registryPda); + if (!account) + throw new Error(`TIN ${String(params.tin)} registry account was not found`); + const registry = decodeTinsIdentityRegistry(account.data); + const socialIdentities = await Promise.all(registry.socialIdentities.map(async (identity) => ({ + type: identity.identityType, + label: identity.label, + value: await decryptTinSocialIdentity({ + tin: params.tin, + nonce: identity.nonce, + ciphertext: identity.ciphertext, + }), + metadata: parseMetadata(identity.metadata), + verifiedBy: identity.verifiedBy?.toBase58() ?? null, + linkedAt: identity.linkedAt.toString(), + }))); + const sensitiveFields = []; + for (const field of registry.sensitiveFields) { + const signature = params.sensitiveAuthorizations?.[field.fieldType]; + if (!signature) + continue; + sensitiveFields.push({ + type: field.fieldType, + value: await decryptTinSensitiveField({ + tin: params.tin, + fieldType: field.fieldType, + nonce: field.nonce, + ciphertext: field.ciphertext, + userSignature: signature, + }), + metadata: parseMetadata(field.metadata), + linkedAt: field.linkedAt.toString(), + }); + } + return { + tin: String(params.tin), + name: registry.name, + authority: registry.authority, + socialIdentities, + sensitiveFields, + encryptedSensitiveFields: registry.sensitiveFields, + }; +} diff --git a/tsn-sdk/dist/token-registry.d.ts b/tsn-sdk/dist/token-registry.d.ts new file mode 100644 index 00000000..ffeb7fb8 --- /dev/null +++ b/tsn-sdk/dist/token-registry.d.ts @@ -0,0 +1,9 @@ +export type TsnAllowedToken = { + mintAddress: string; + symbol: string; + name: string; + decimals?: number; +}; +export declare function tsnGetAllowedSplTokens(env: Record): TsnAllowedToken[]; +export declare function tsnResolveSplTokenInput(tokenInput: string, env: Record): TsnAllowedToken; +//# sourceMappingURL=token-registry.d.ts.map \ No newline at end of file diff --git a/tsn-sdk/dist/token-registry.d.ts.map b/tsn-sdk/dist/token-registry.d.ts.map new file mode 100644 index 00000000..5c3f786c --- /dev/null +++ b/tsn-sdk/dist/token-registry.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"token-registry.d.ts","sourceRoot":"","sources":["../src/token-registry.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAiCF,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACtC,eAAe,EAAE,CAanB;AAED,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACtC,eAAe,CAYjB"} \ No newline at end of file diff --git a/tsn-sdk/dist/token-registry.js b/tsn-sdk/dist/token-registry.js new file mode 100644 index 00000000..6d77d743 --- /dev/null +++ b/tsn-sdk/dist/token-registry.js @@ -0,0 +1,61 @@ +const DEVNET_USDC_MINT = "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"; +const DEFAULT_ALLOWED_TOKENS = [ + { + mintAddress: DEVNET_USDC_MINT, + symbol: "USDC", + name: "USD Coin", + decimals: 6, + }, +]; +function normalizeToken(input) { + if (!input || typeof input !== "object") + return null; + const candidate = input; + const mintAddress = String(candidate.mintAddress ?? "").trim(); + if (!mintAddress) + return null; + const symbol = String(candidate.symbol ?? "").trim().toUpperCase(); + const name = String(candidate.name ?? "").trim(); + const decimalsRaw = candidate.decimals; + const decimals = typeof decimalsRaw === "number" && Number.isFinite(decimalsRaw) + ? decimalsRaw + : undefined; + return { + mintAddress, + symbol: symbol || mintAddress.slice(0, 6), + name: name || symbol || "Unknown Token", + ...(decimals !== undefined ? { decimals } : {}), + }; +} +export function tsnGetAllowedSplTokens(env) { + const raw = env.SOLANA_ALLOWED_SPL_TOKENS?.trim(); + if (!raw) + return DEFAULT_ALLOWED_TOKENS; + try { + const parsed = JSON.parse(raw); + if (!Array.isArray(parsed)) + return DEFAULT_ALLOWED_TOKENS; + const normalized = parsed + .map((entry) => normalizeToken(entry)) + .filter((entry) => entry !== null); + return normalized.length > 0 ? normalized : DEFAULT_ALLOWED_TOKENS; + } + catch { + return DEFAULT_ALLOWED_TOKENS; + } +} +export function tsnResolveSplTokenInput(tokenInput, env) { + const trimmed = tokenInput.trim(); + const tokens = tsnGetAllowedSplTokens(env); + const byMint = tokens.find((token) => token.mintAddress === trimmed); + if (byMint) + return byMint; + const bySymbol = tokens.find((token) => token.symbol === trimmed.toUpperCase()); + if (bySymbol) + return bySymbol; + return { + mintAddress: trimmed, + symbol: trimmed.slice(0, 6).toUpperCase(), + name: "Custom Token", + }; +} diff --git a/tsn-sdk/package.json b/tsn-sdk/package.json index a90a74f0..a4e55253 100644 --- a/tsn-sdk/package.json +++ b/tsn-sdk/package.json @@ -65,6 +65,10 @@ "./server": { "import": "./dist/server.js", "types": "./dist/server.d.ts" + }, + "./settlement-token": { + "import": "./dist/settlement-token.js", + "types": "./dist/settlement-token.d.ts" } }, "scripts": { diff --git a/tsn-sdk/src/contracts.ts b/tsn-sdk/src/contracts.ts index 3e843155..64827a17 100644 --- a/tsn-sdk/src/contracts.ts +++ b/tsn-sdk/src/contracts.ts @@ -3,6 +3,8 @@ import { bytesToHex, utf8ToBytes } from "@noble/hashes/utils"; export type TsnIntentStatus = "pending" | "escrowed" | "onchain" | "claimed" | "executed" | "settled" | "expired" | "failed" | "canceled" | "reverted"; export type TsnClaimRequestStatus = "pending" | "processing" | "completed" | "failed" | "canceled"; +export type TsnLeaseStatus = "active" | "completed" | "expired" | "canceled"; +export type TsnRecoveryJobStatus = "open" | "leased" | "completed" | "failed" | "canceled"; export type TsnUiStage = "intent_pending" | "claim_requested" | "escrowed" | "lease_claimed" | "cranker_paid" | "epoch_settled" | "reverted"; export type PaymentIntentStatus = "pending" | "escrowed" | "onchain" | "claimed" | "executed" | "settled" | "expired" | "failed" | "canceled" | "reverted"; @@ -59,6 +61,71 @@ export type CreateIntentRequest = { amount: number; recipientAmount?: number; source?: string; + epoch?: number; + encryptedSettlementToken?: string; + settlementTokenCommitmentHash?: string; + commitmentRegistryEntry?: CommitmentRegistryEntry; +}; + +export type CommitmentRegistryEntry = { + transferId: string; + encryptedSettlementToken: string; + commitmentHash: string; + timestamp: string; + epoch: number; + recoverable: boolean; + intentVerifierPubkey?: string | null; + settlementCommitmentHash?: string | null; + settlementProofTx?: string | null; + otdtHash?: string | null; + recoveryProofTx?: string | null; + updatedAt?: string; +}; + +export type ClaimPointLedgerEntry = { + crankerPubkey: string; + earned: number; + available: number; + leased: number; + lastIntentWorkAt?: string | null; +}; + +export type ClaimLeaseRecord = { + id: string; + transferId: string; + crankerPubkey: string; + status: TsnLeaseStatus; + pointsSpent: number; + otdtHash?: string | null; + issuedAt: string; + expiresAt: string; + completedAt?: string | null; +}; + +export type RecoveryQueueEntry = { + id: string; + transferId: string; + epoch: number; + recoverableAmount: number; + vaultSource: string; + recoveryReward: number; + priorityScore: number; + status: TsnRecoveryJobStatus; + leasedByCrankerPubkey?: string | null; + leaseExpiresAt?: string | null; + proofTx?: string | null; + createdAt: string; + updatedAt: string; +}; + +export type LiquidityMetrics = { + activeLiquidity: number; + pendingIntentAmount: number; + vaultBalance: number; + settlementVelocity: number; + liquidityConsumptionRate: number; + lowLiquidityThreshold: number; + updatedAt: string; }; export type RequestClaimRequest = { @@ -79,6 +146,7 @@ export type TsnMempoolIntent = CreateIntentRequest & { proofTxSig?: string | null; settlementResolution?: "completed" | "reverted" | null; settlementReason?: string | null; + claimLeaseId?: string | null; postedAt: string; updatedAt: string; }; @@ -87,6 +155,7 @@ export type TsnMempoolClaimRequest = RequestClaimRequest & { id: string; status: TsnClaimRequestStatus; settlementReason?: string | null; + claimLeaseId?: string | null; postedAt: string; updatedAt: string; }; @@ -106,6 +175,8 @@ export type ProofOfPaymentRequest = { cranker_pubkey: string; proof_tx: string; encrypted_payload?: string | null; + settlement_commitment_hash?: string | null; + otdt_hash?: string | null; }; export type IntentState = { @@ -141,6 +212,9 @@ export function buildCreateIntentRequest(params: { tokenMintAddress: string; amount: number; source?: string; + epoch?: number; + encryptedSettlementToken?: string; + settlementTokenCommitmentHash?: string; }): CreateIntentRequest { return { paymentId: params.paymentId, @@ -164,6 +238,9 @@ export function buildCreateIntentRequest(params: { tokenMintAddress: params.tokenMintAddress, amount: params.amount, source: params.source, + epoch: params.epoch, + encryptedSettlementToken: params.encryptedSettlementToken, + settlementTokenCommitmentHash: params.settlementTokenCommitmentHash, }; } diff --git a/tsn-sdk/src/index.ts b/tsn-sdk/src/index.ts index 9057b37b..d61de66a 100644 --- a/tsn-sdk/src/index.ts +++ b/tsn-sdk/src/index.ts @@ -16,3 +16,4 @@ export * from "./sponsored-settlement.js"; export * from "./tins.js"; export * from "./blockchain/solana-core.js"; export * from "./blockchain/solana-tsn.js"; +export * from "./settlement-token.js"; diff --git a/tsn-sdk/src/mempool.ts b/tsn-sdk/src/mempool.ts index 9fc6e6e6..0b452859 100644 --- a/tsn-sdk/src/mempool.ts +++ b/tsn-sdk/src/mempool.ts @@ -3,7 +3,13 @@ import { mkdir, readFile, writeFile } from "node:fs/promises"; import { dirname, resolve } from "node:path"; import type { + ClaimLeaseRecord, + ClaimPointLedgerEntry, + CommitmentRegistryEntry, CreateIntentRequest, + LiquidityMetrics, + ProofOfPaymentRequest, + RecoveryQueueEntry, RequestClaimRequest, TsnClaimRequestStatus, TsnIntentStatus, @@ -11,9 +17,14 @@ import type { TsnMempoolIntent, TsnIntentWorkItem, TsnWorkItem, - ProofOfPaymentRequest, } from "./contracts.js"; import { TsnHttpClient } from "./client.js"; +import { + createEncryptedSettlementToken, + createOneTimeDecryptionToken, + currentTsnEpoch, + settlementSha256Hex, +} from "./settlement-token.js"; export interface TsnMempool { postIntent(request: CreateIntentRequest): Promise; @@ -22,6 +33,14 @@ export interface TsnMempool { listClaimRequests(params?: { intentId?: string; status?: TsnClaimRequestStatus }): Promise; listPendingIntentWork(limit?: number): Promise; listPendingWork(limit?: number): Promise; + listCommitmentRegistry(): Promise; + listClaimPointLedger(): Promise; + listClaimLeases(): Promise; + listRecoveryQueue(params?: { status?: RecoveryQueueEntry["status"] }): Promise; + getLiquidityMetrics(): Promise; + submitIntentVerification(id: string, crankerPubkey: string, patch?: Partial): Promise; + acquireClaimLease(intentId: string, crankerPubkey: string): Promise; + completeRecoveryJob(jobId: string, crankerPubkey: string, proofTx: string): Promise; updateIntentStatus(id: string, status: TsnIntentStatus, patch?: Partial): Promise; updateClaimRequestStatus( id: string, @@ -35,18 +54,45 @@ type Snapshot = { intents: TsnMempoolIntent[]; claimRequests: TsnMempoolClaimRequest[]; proofs?: ProofOfPaymentRequest[]; + commitmentRegistry?: CommitmentRegistryEntry[]; + claimPointLedger?: ClaimPointLedgerEntry[]; + claimLeases?: ClaimLeaseRecord[]; + recoveryQueue?: RecoveryQueueEntry[]; + liquidityMetrics?: LiquidityMetrics; }; function now() { return new Date().toISOString(); } +function defaultLiquidityMetrics(): LiquidityMetrics { + return { + activeLiquidity: Number(process.env.TSN_ACTIVE_LIQUIDITY ?? 0), + pendingIntentAmount: 0, + vaultBalance: Number(process.env.TSN_VAULT_BALANCE ?? 0), + settlementVelocity: 0, + liquidityConsumptionRate: 0, + lowLiquidityThreshold: Number(process.env.TSN_LOW_LIQUIDITY_THRESHOLD ?? 100), + updatedAt: now(), + }; +} + +function normalizeSnapshot(snapshot: Snapshot): Snapshot { + snapshot.proofs ??= []; + snapshot.commitmentRegistry ??= []; + snapshot.claimPointLedger ??= []; + snapshot.claimLeases ??= []; + snapshot.recoveryQueue ??= []; + snapshot.liquidityMetrics ??= defaultLiquidityMetrics(); + return snapshot; +} + async function readSnapshot(path: string): Promise { try { - return JSON.parse(await readFile(path, "utf8")) as Snapshot; + return normalizeSnapshot(JSON.parse(await readFile(path, "utf8")) as Snapshot); } catch (error) { if ((error as NodeJS.ErrnoException).code === "ENOENT") { - return { intents: [], claimRequests: [], proofs: [] }; + return normalizeSnapshot({ intents: [], claimRequests: [], proofs: [] }); } throw error; } @@ -54,7 +100,75 @@ async function readSnapshot(path: string): Promise { async function writeSnapshot(path: string, snapshot: Snapshot) { await mkdir(dirname(path), { recursive: true }); - await writeFile(path, `${JSON.stringify(snapshot, null, 2)}\n`, "utf8"); + await writeFile(path, `${JSON.stringify(normalizeSnapshot(snapshot), null, 2)}\n`, "utf8"); +} + +function ensureSettlementToken(request: CreateIntentRequest): CreateIntentRequest { + if (request.encryptedSettlementToken && request.settlementTokenCommitmentHash) return request; + const epoch = request.epoch ?? currentTsnEpoch(); + const bundle = createEncryptedSettlementToken({ + transferId: request.paymentId, + recipientHash: request.recipientHash, + tokenMintAddress: request.tokenMintAddress, + amount: request.amount, + epoch, + }); + return { + ...request, + epoch, + encryptedSettlementToken: bundle.encryptedSettlementToken, + settlementTokenCommitmentHash: bundle.commitmentHash, + }; +} + +function registryEntryForIntent(intent: TsnMempoolIntent): CommitmentRegistryEntry { + if (!intent.encryptedSettlementToken || !intent.settlementTokenCommitmentHash) { + throw new Error("TSN intent is missing encrypted settlement token commitment"); + } + return { + transferId: intent.id, + encryptedSettlementToken: intent.encryptedSettlementToken, + commitmentHash: intent.settlementTokenCommitmentHash, + timestamp: intent.postedAt, + epoch: intent.epoch ?? currentTsnEpoch(), + recoverable: false, + updatedAt: now(), + }; +} + +function getLedger(snapshot: Snapshot, crankerPubkey: string) { + snapshot.claimPointLedger ??= []; + let entry = snapshot.claimPointLedger.find((candidate) => candidate.crankerPubkey === crankerPubkey); + if (!entry) { + entry = { crankerPubkey, earned: 0, available: 0, leased: 0, lastIntentWorkAt: null }; + snapshot.claimPointLedger.push(entry); + } + return entry; +} + +function refreshLiquidityMetrics(snapshot: Snapshot) { + snapshot.liquidityMetrics ??= defaultLiquidityMetrics(); + const pendingIntentAmount = snapshot.intents + .filter((intent) => ["pending", "escrowed", "onchain", "claimed"].includes(intent.status)) + .reduce((total, intent) => total + Number(intent.amount || 0), 0); + const executedCount = snapshot.intents.filter((intent) => intent.status === "executed" || intent.status === "settled").length; + const activeLiquidity = Math.max(0, Number(snapshot.liquidityMetrics.activeLiquidity || 0)); + const vaultBalance = Math.max(0, Number(snapshot.liquidityMetrics.vaultBalance || activeLiquidity)); + snapshot.liquidityMetrics = { + ...snapshot.liquidityMetrics, + activeLiquidity, + vaultBalance, + pendingIntentAmount, + settlementVelocity: executedCount, + liquidityConsumptionRate: activeLiquidity > 0 ? pendingIntentAmount / activeLiquidity : pendingIntentAmount, + updatedAt: now(), + }; + return snapshot.liquidityMetrics; +} + +function recoveryPriority(metrics: LiquidityMetrics, jobAmount: number) { + const deficit = Math.max(0, metrics.lowLiquidityThreshold - metrics.activeLiquidity); + return Number((deficit * 10 + metrics.pendingIntentAmount * 2 + metrics.settlementVelocity + jobAmount).toFixed(6)); } export class JsonFileTsnMempool implements TsnMempool { @@ -70,14 +184,17 @@ export class JsonFileTsnMempool implements TsnMempool { if (existing) return existing; const timestamp = now(); + const securedRequest = ensureSettlementToken(request); const intent: TsnMempoolIntent = { - ...request, - id: request.paymentId, + ...securedRequest, + id: securedRequest.paymentId, status: "pending", postedAt: timestamp, updatedAt: timestamp, }; snapshot.intents.push(intent); + snapshot.commitmentRegistry!.push(registryEntryForIntent(intent)); + refreshLiquidityMetrics(snapshot); await writeSnapshot(this.path, snapshot); return intent; } @@ -98,6 +215,7 @@ export class JsonFileTsnMempool implements TsnMempool { updatedAt: timestamp, }; snapshot.claimRequests.push(claimRequest); + refreshLiquidityMetrics(snapshot); await writeSnapshot(this.path, snapshot); return claimRequest; } @@ -142,11 +260,97 @@ export class JsonFileTsnMempool implements TsnMempool { .map((intent) => ({ intent })); } + async listCommitmentRegistry() { + return (await readSnapshot(this.path)).commitmentRegistry!; + } + + async listClaimPointLedger() { + return (await readSnapshot(this.path)).claimPointLedger!; + } + + async listClaimLeases() { + return (await readSnapshot(this.path)).claimLeases!; + } + + async listRecoveryQueue(params: { status?: RecoveryQueueEntry["status"] } = {}) { + const queue = (await readSnapshot(this.path)).recoveryQueue!; + return params.status ? queue.filter((entry) => entry.status === params.status) : queue; + } + + async getLiquidityMetrics() { + const snapshot = await readSnapshot(this.path); + const metrics = refreshLiquidityMetrics(snapshot); + await writeSnapshot(this.path, snapshot); + return metrics; + } + + async submitIntentVerification(id: string, crankerPubkey: string, patch: Partial = {}) { + const snapshot = await readSnapshot(this.path); + const intent = snapshot.intents.find((candidate) => candidate.id === id); + if (!intent) return null; + if (!intent.encryptedSettlementToken || !intent.settlementTokenCommitmentHash) { + throw new Error("Intent verification failed: encrypted settlement token commitment is missing"); + } + const registryEntry = snapshot.commitmentRegistry!.find((entry) => entry.transferId === id); + if (!registryEntry) snapshot.commitmentRegistry!.push(registryEntryForIntent(intent)); + Object.assign(intent, patch, { + status: "escrowed" as TsnIntentStatus, + assignedCrankerPubkey: crankerPubkey, + updatedAt: now(), + }); + const ledger = getLedger(snapshot, crankerPubkey); + ledger.earned += 1; + ledger.available += 1; + ledger.lastIntentWorkAt = now(); + refreshLiquidityMetrics(snapshot); + await writeSnapshot(this.path, snapshot); + return intent; + } + + async acquireClaimLease(intentId: string, crankerPubkey: string) { + const snapshot = await readSnapshot(this.path); + const registryEntry = snapshot.commitmentRegistry!.find((entry) => entry.transferId === intentId); + if (!registryEntry) throw new Error("Commitment registry entry not found"); + if (registryEntry.recoverable) throw new Error("Transfer is already recoverable; no claim lease is permitted"); + if (registryEntry.otdtHash) throw new Error("OTDT has already been issued for this transfer"); + const existingLease = snapshot.claimLeases!.find((lease) => lease.transferId === intentId && lease.status === "active" && Date.parse(lease.expiresAt) > Date.now()); + if (existingLease) return existingLease; + const ledger = getLedger(snapshot, crankerPubkey); + if (ledger.available < 1) throw new Error("Cranker has no available claim points for a claim lease"); + ledger.available -= 1; + ledger.leased += 1; + const issuedAt = now(); + const lease: ClaimLeaseRecord = { + id: randomUUID(), + transferId: intentId, + crankerPubkey, + status: "active", + pointsSpent: 1, + issuedAt, + expiresAt: new Date(Date.parse(issuedAt) + Number(process.env.TSN_CLAIM_LEASE_TTL_MS ?? 10 * 60_000)).toISOString(), + }; + const otdt = createOneTimeDecryptionToken({ + transferId: intentId, + leaseId: lease.id, + crankerPubkey, + commitmentHash: registryEntry.commitmentHash, + }); + lease.otdtHash = otdt.tokenHash; + registryEntry.otdtHash = otdt.tokenHash; + registryEntry.updatedAt = now(); + snapshot.claimLeases!.push(lease); + const intent = snapshot.intents.find((candidate) => candidate.id === intentId); + if (intent) Object.assign(intent, { status: "claimed" as TsnIntentStatus, claimLeaseId: lease.id, updatedAt: now() }); + await writeSnapshot(this.path, snapshot); + return lease; + } + async updateIntentStatus(id: string, status: TsnIntentStatus, patch: Partial = {}) { const snapshot = await readSnapshot(this.path); const intent = snapshot.intents.find((candidate) => candidate.id === id); if (!intent) return null; Object.assign(intent, patch, { status, updatedAt: now() }); + refreshLiquidityMetrics(snapshot); await writeSnapshot(this.path, snapshot); return intent; } @@ -162,9 +366,21 @@ export class JsonFileTsnMempool implements TsnMempool { async postProof(request: ProofOfPaymentRequest): Promise { const snapshot = await readSnapshot(this.path); - if (!snapshot.proofs) snapshot.proofs = []; - snapshot.proofs.push(request); + snapshot.proofs!.push(request); const intent = snapshot.intents.find((candidate) => candidate.id === request.intent_id); + const registryEntry = snapshot.commitmentRegistry!.find((candidate) => candidate.transferId === request.intent_id); + if (!registryEntry) throw new Error("Commitment registry entry not found for proof"); + if (registryEntry.recoverable) throw new Error("Transfer is already recoverable"); + if (registryEntry.otdtHash && request.otdt_hash && registryEntry.otdtHash !== request.otdt_hash) { + throw new Error("OTDT hash mismatch for settlement proof"); + } + const settlementCommitmentHash = request.settlement_commitment_hash ?? settlementSha256Hex(`${request.intent_id}:${request.proof_tx}:${request.cranker_pubkey}`); + Object.assign(registryEntry, { + settlementCommitmentHash, + settlementProofTx: request.proof_tx, + recoverable: true, + updatedAt: now(), + }); if (intent && ["escrowed", "onchain", "claimed"].includes(intent.status)) { Object.assign(intent, { status: "executed" as TsnIntentStatus, @@ -172,9 +388,52 @@ export class JsonFileTsnMempool implements TsnMempool { updatedAt: now(), }); } + const lease = snapshot.claimLeases!.find((candidate) => candidate.transferId === request.intent_id && candidate.status === "active"); + if (lease) Object.assign(lease, { status: "completed", completedAt: now() }); + const claim = snapshot.claimRequests.find((candidate) => candidate.intentId === request.intent_id && candidate.status !== "completed"); + if (claim) Object.assign(claim, { status: "completed" as TsnClaimRequestStatus, updatedAt: now() }); + const metrics = refreshLiquidityMetrics(snapshot); + if (!snapshot.recoveryQueue!.some((entry) => entry.transferId === request.intent_id)) { + const amount = Number(intent?.amount ?? 0); + const reward = Number((amount * Number(process.env.TSN_RECOVERY_REWARD_BPS ?? 200) / 10_000).toFixed(9)); + const timestamp = now(); + snapshot.recoveryQueue!.push({ + id: randomUUID(), + transferId: request.intent_id, + epoch: registryEntry.epoch, + recoverableAmount: amount, + vaultSource: `commitment:${registryEntry.commitmentHash}`, + recoveryReward: reward, + priorityScore: recoveryPriority(metrics, amount), + status: "open", + createdAt: timestamp, + updatedAt: timestamp, + }); + } await writeSnapshot(this.path, snapshot); return request; } + + async completeRecoveryJob(jobId: string, crankerPubkey: string, proofTx: string) { + const snapshot = await readSnapshot(this.path); + const job = snapshot.recoveryQueue!.find((candidate) => candidate.id === jobId); + if (!job || job.status === "completed") return job ?? null; + const timestamp = now(); + Object.assign(job, { + status: "completed" as const, + leasedByCrankerPubkey: crankerPubkey, + proofTx, + updatedAt: timestamp, + }); + const registryEntry = snapshot.commitmentRegistry!.find((entry) => entry.transferId === job.transferId); + if (registryEntry) Object.assign(registryEntry, { recoveryProofTx: proofTx, updatedAt: timestamp }); + snapshot.liquidityMetrics ??= defaultLiquidityMetrics(); + snapshot.liquidityMetrics.activeLiquidity += job.recoverableAmount; + snapshot.liquidityMetrics.vaultBalance += job.recoverableAmount; + snapshot.liquidityMetrics.updatedAt = timestamp; + await writeSnapshot(this.path, snapshot); + return job; + } } export class HttpTsnMempool implements TsnMempool { @@ -218,6 +477,47 @@ export class HttpTsnMempool implements TsnMempool { return this.client.listPendingIntentWork(limit); } + listCommitmentRegistry(): Promise { + return this.client.get("/commitment-registry"); + } + + listClaimPointLedger(): Promise { + return this.client.get("/claim-points"); + } + + listClaimLeases(): Promise { + return this.client.get("/claim-leases"); + } + + listRecoveryQueue(params: { status?: RecoveryQueueEntry["status"] } = {}): Promise { + const search = new URLSearchParams(); + if (params.status) search.set("status", params.status); + const query = search.toString(); + return this.client.get(`/recovery-queue${query ? `?${query}` : ""}`); + } + + getLiquidityMetrics(): Promise { + return this.client.get("/liquidity-metrics"); + } + + submitIntentVerification(id: string, crankerPubkey: string, patch: Partial = {}) { + return this.client.post & { crankerPubkey: string }, TsnMempoolIntent>(`/intents/${encodeURIComponent(id)}/verify`, { + ...patch, + crankerPubkey, + }); + } + + acquireClaimLease(intentId: string, crankerPubkey: string) { + return this.client.post<{ crankerPubkey: string }, ClaimLeaseRecord>(`/intents/${encodeURIComponent(intentId)}/claim-lease`, { crankerPubkey }); + } + + completeRecoveryJob(jobId: string, crankerPubkey: string, proofTx: string) { + return this.client.post<{ crankerPubkey: string; proofTx: string }, RecoveryQueueEntry>(`/recovery-queue/${encodeURIComponent(jobId)}/complete`, { + crankerPubkey, + proofTx, + }); + } + updateIntentStatus(id: string, status: TsnIntentStatus, patch: Partial = {}) { return this.client.updateIntentStatus & { status: TsnIntentStatus }, TsnMempoolIntent>(id, { ...patch, diff --git a/tsn-sdk/src/server.ts b/tsn-sdk/src/server.ts index 4de67745..8e1c0c2b 100644 --- a/tsn-sdk/src/server.ts +++ b/tsn-sdk/src/server.ts @@ -10,7 +10,8 @@ async function readJson(request: IncomingMessage) { for await (const chunk of request) { chunks.push(Buffer.from(chunk)); } - return JSON.parse(Buffer.concat(chunks).toString("utf8")); + const body = Buffer.concat(chunks).toString("utf8"); + return body ? JSON.parse(body) : {}; } function send(response: ServerResponse, status: number, body: unknown) { @@ -18,22 +19,85 @@ function send(response: ServerResponse, status: number, body: unknown) { response.end(JSON.stringify(body)); } +function routeUrl(request: IncomingMessage) { + return new URL(request.url ?? "/", `http://localhost:${port}`); +} + createServer(async (request, response) => { try { - if (request.method === "POST" && request.url === "/intents") { + const url = routeUrl(request); + const path = url.pathname; + + if (request.method === "POST" && path === "/intents") { return send(response, 200, await mempool.postIntent(await readJson(request))); } - if (request.method === "POST" && request.url === "/claim-requests") { + if (request.method === "GET" && path === "/intents") { + return send(response, 200, await mempool.listIntents({ status: url.searchParams.get("status") as any })); + } + if (request.method === "POST" && path === "/claim-requests") { return send(response, 200, await mempool.postClaimRequest(await readJson(request))); } - if (request.method === "GET" && request.url?.startsWith("/work")) { - const url = new URL(request.url, `http://localhost:${port}`); - return send(response, 200, { intents: await mempool.listPendingWork(Number(url.searchParams.get("limit") ?? 50)) }); + if (request.method === "GET" && path === "/claim-requests") { + return send(response, 200, await mempool.listClaimRequests({ + intentId: url.searchParams.get("intent_id") ?? undefined, + status: url.searchParams.get("status") as any, + })); + } + if (request.method === "GET" && path === "/work") { + return send(response, 200, await mempool.listPendingWork(Number(url.searchParams.get("limit") ?? 50))); + } + if (request.method === "GET" && path === "/intent-work") { + return send(response, 200, await mempool.listPendingIntentWork(Number(url.searchParams.get("limit") ?? 50))); + } + if (request.method === "GET" && path === "/commitment-registry") { + return send(response, 200, await mempool.listCommitmentRegistry()); + } + if (request.method === "GET" && path === "/claim-points") { + return send(response, 200, await mempool.listClaimPointLedger()); + } + if (request.method === "GET" && path === "/claim-leases") { + return send(response, 200, await mempool.listClaimLeases()); + } + if (request.method === "GET" && path === "/recovery-queue") { + return send(response, 200, await mempool.listRecoveryQueue({ status: url.searchParams.get("status") as any })); } + if (request.method === "GET" && path === "/liquidity-metrics") { + return send(response, 200, await mempool.getLiquidityMetrics()); + } + if (request.method === "POST" && path === "/proofs") { + return send(response, 200, await mempool.postProof(await readJson(request))); + } + + const verifyMatch = path.match(/^\/intents\/([^/]+)\/verify$/); + if (request.method === "POST" && verifyMatch) { + const body = await readJson(request); + return send(response, 200, await mempool.submitIntentVerification(decodeURIComponent(verifyMatch[1]), body.crankerPubkey, body)); + } + const leaseMatch = path.match(/^\/intents\/([^/]+)\/claim-lease$/); + if (request.method === "POST" && leaseMatch) { + const body = await readJson(request); + return send(response, 200, await mempool.acquireClaimLease(decodeURIComponent(leaseMatch[1]), body.crankerPubkey)); + } + const intentStatusMatch = path.match(/^\/intents\/([^/]+)\/status$/); + if (request.method === "PATCH" && intentStatusMatch) { + const body = await readJson(request); + return send(response, 200, await mempool.updateIntentStatus(decodeURIComponent(intentStatusMatch[1]), body.status, body)); + } + const claimStatusMatch = path.match(/^\/claim-requests\/([^/]+)\/status$/); + if (request.method === "PATCH" && claimStatusMatch) { + const body = await readJson(request); + return send(response, 200, await mempool.updateClaimRequestStatus(decodeURIComponent(claimStatusMatch[1]), body.status, body)); + } + const recoveryCompleteMatch = path.match(/^\/recovery-queue\/([^/]+)\/complete$/); + if (request.method === "POST" && recoveryCompleteMatch) { + const body = await readJson(request); + return send(response, 200, await mempool.completeRecoveryJob(decodeURIComponent(recoveryCompleteMatch[1]), body.crankerPubkey, body.proofTx)); + } + return send(response, 404, { error: "Not found" }); } catch (error) { return send(response, 500, { error: error instanceof Error ? error.message : "Unknown error" }); } }).listen(port, () => { console.log(`[tsn-mempool] listening on http://localhost:${port}`); -}); \ No newline at end of file +}); diff --git a/tsn-sdk/src/settlement-token.ts b/tsn-sdk/src/settlement-token.ts new file mode 100644 index 00000000..9cc46a54 --- /dev/null +++ b/tsn-sdk/src/settlement-token.ts @@ -0,0 +1,256 @@ +import { createHash, createHmac, randomBytes, timingSafeEqual } from "node:crypto"; + +export const TSN_SETTLEMENT_TOKEN_VERSION = 1; +export const TSN_SETTLEMENT_TOKEN_ALGORITHM = "TSN-HKDF-SHA256-STREAM-HMAC"; + +export type SettlementTokenPlaintext = { + transferId: string; + recipientHash: string; + tokenMintAddress: string; + amount: number; + epoch: number; + nonce: string; + issuedAt: string; +}; + +export type EncryptedSettlementToken = { + version: number; + algorithm: typeof TSN_SETTLEMENT_TOKEN_ALGORITHM; + salt: string; + nonce: string; + aad: string; + aadHash: string; + authorizedCrankerDnaHash: string; + ciphertext: string; + tag: string; +}; + +export type SettlementTokenBundle = { + plaintext: SettlementTokenPlaintext; + encryptedSettlementToken: string; + commitmentHash: string; +}; + +export type OneTimeDecryptionToken = { + id: string; + transferId: string; + leaseId: string; + crankerPubkey: string; + commitmentHash: string; + issuedAt: string; + expiresAt: string; + tokenHash: string; +}; + +function base64UrlEncode(bytes: Buffer | Uint8Array) { + return Buffer.from(bytes) + .toString("base64") + .replace(/=/g, "") + .replace(/\+/g, "-") + .replace(/\//g, "_"); +} + +function base64UrlDecode(value: string) { + const padded = value.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - (value.length % 4)) % 4); + return Buffer.from(padded, "base64"); +} + +export function settlementSha256Hex(input: string | Buffer | Uint8Array) { + return createHash("sha256").update(input).digest("hex"); +} + +export function canonicalJson(value: unknown): string { + if (value == null || typeof value !== "object") return JSON.stringify(value); + if (Array.isArray(value)) return `[${value.map((entry) => canonicalJson(entry)).join(",")}]`; + return `{${Object.entries(value as Record) + .sort(([left], [right]) => left.localeCompare(right)) + .map(([key, entry]) => `${JSON.stringify(key)}:${canonicalJson(entry)}`) + .join(",")}}`; +} + +export function settlementTokenCommitmentHash(plaintext: SettlementTokenPlaintext) { + return settlementSha256Hex(canonicalJson(plaintext)); +} + +export function crankerDnaHash(value: string) { + return settlementSha256Hex(`tsn-cranker-dna:${value}`); +} + +function normalizeMasterKey(masterKey?: string | Buffer | Uint8Array) { + const source = masterKey ?? process.env.TSN_SETTLEMENT_TOKEN_MASTER_KEY; + if (!source) { + throw new Error("TSN_SETTLEMENT_TOKEN_MASTER_KEY is required for settlement-token encryption"); + } + if (Buffer.isBuffer(source) || source instanceof Uint8Array) return Buffer.from(source); + const trimmed = source.trim(); + if (/^[0-9a-f]{64}$/i.test(trimmed)) return Buffer.from(trimmed, "hex"); + try { + const decoded = Buffer.from(trimmed, "base64"); + if (decoded.length >= 32) return decoded; + } catch { + // fall through to hash-based normalization + } + return Buffer.from(settlementSha256Hex(trimmed), "hex"); +} + +function hmac(key: Buffer, value: string | Buffer | Uint8Array) { + return createHmac("sha256", key).update(value).digest(); +} + +function deriveKeys(params: { + masterKey?: string | Buffer | Uint8Array; + transferId: string; + salt: string; + authorizedCrankerDnaHash: string; +}) { + const master = normalizeMasterKey(params.masterKey); + const prk = hmac(master, Buffer.from(`tsn-settlement:${params.transferId}:${params.salt}`, "utf8")); + const encKey = hmac(prk, `enc:${params.authorizedCrankerDnaHash}`); + const macKey = hmac(prk, `mac:${params.authorizedCrankerDnaHash}`); + return { encKey, macKey }; +} + +function streamXor(input: Buffer, key: Buffer, nonce: Buffer) { + const output = Buffer.alloc(input.length); + let offset = 0; + let counter = 0; + while (offset < input.length) { + const counterBuffer = Buffer.alloc(4); + counterBuffer.writeUInt32BE(counter, 0); + const block = hmac(key, Buffer.concat([nonce, counterBuffer])); + for (let index = 0; index < block.length && offset < input.length; index += 1, offset += 1) { + output[offset] = input[offset] ^ block[index]; + } + counter += 1; + } + return output; +} + +export function createEncryptedSettlementToken(params: { + transferId: string; + recipientHash: string; + tokenMintAddress: string; + amount: number; + epoch: number; + authorizedCrankerDnaHash?: string | null; + masterKey?: string | Buffer | Uint8Array; + issuedAt?: string; +}): SettlementTokenBundle { + const plaintext: SettlementTokenPlaintext = { + transferId: params.transferId, + recipientHash: params.recipientHash, + tokenMintAddress: params.tokenMintAddress, + amount: params.amount, + epoch: params.epoch, + nonce: base64UrlEncode(randomBytes(24)), + issuedAt: params.issuedAt ?? new Date().toISOString(), + }; + const salt = base64UrlEncode(randomBytes(16)); + const nonce = randomBytes(24); + const authorizedCrankerDnaHash = params.authorizedCrankerDnaHash ?? crankerDnaHash(process.env.TSN_CRANKER_DNA ?? "trustlink-authorized-cranker"); + const aad = canonicalJson({ + transferId: params.transferId, + commitmentHash: settlementTokenCommitmentHash(plaintext), + epoch: params.epoch, + authorizedCrankerDnaHash, + }); + const { encKey, macKey } = deriveKeys({ + masterKey: params.masterKey, + transferId: params.transferId, + salt, + authorizedCrankerDnaHash, + }); + const plaintextBytes = Buffer.from(canonicalJson(plaintext), "utf8"); + const ciphertext = streamXor(plaintextBytes, encKey, nonce); + const tag = hmac(macKey, Buffer.concat([Buffer.from(aad, "utf8"), nonce, ciphertext])); + const envelope: EncryptedSettlementToken = { + version: TSN_SETTLEMENT_TOKEN_VERSION, + algorithm: TSN_SETTLEMENT_TOKEN_ALGORITHM, + salt, + nonce: base64UrlEncode(nonce), + aad: base64UrlEncode(Buffer.from(aad, "utf8")), + aadHash: settlementSha256Hex(aad), + authorizedCrankerDnaHash, + ciphertext: base64UrlEncode(ciphertext), + tag: base64UrlEncode(tag), + }; + + return { + plaintext, + encryptedSettlementToken: base64UrlEncode(Buffer.from(JSON.stringify(envelope), "utf8")), + commitmentHash: settlementTokenCommitmentHash(plaintext), + }; +} + +export function decodeEncryptedSettlementToken(value: string): EncryptedSettlementToken { + const parsed = JSON.parse(base64UrlDecode(value).toString("utf8")) as EncryptedSettlementToken; + if (parsed.version !== TSN_SETTLEMENT_TOKEN_VERSION || parsed.algorithm !== TSN_SETTLEMENT_TOKEN_ALGORITHM) { + throw new Error("Unsupported TSN settlement token envelope"); + } + return parsed; +} + +export function decryptSettlementToken(params: { + encryptedSettlementToken: string; + transferId: string; + commitmentHash: string; + authorizedCrankerDnaHash?: string | null; + masterKey?: string | Buffer | Uint8Array; +}): SettlementTokenPlaintext { + const envelope = decodeEncryptedSettlementToken(params.encryptedSettlementToken); + const authorizedCrankerDnaHash = params.authorizedCrankerDnaHash ?? envelope.authorizedCrankerDnaHash; + if (authorizedCrankerDnaHash !== envelope.authorizedCrankerDnaHash) { + throw new Error("Cranker DNA is not authorized for this settlement token"); + } + const { encKey, macKey } = deriveKeys({ + masterKey: params.masterKey, + transferId: params.transferId, + salt: envelope.salt, + authorizedCrankerDnaHash, + }); + const nonce = base64UrlDecode(envelope.nonce); + const ciphertext = base64UrlDecode(envelope.ciphertext); + const aad = base64UrlDecode(envelope.aad).toString("utf8"); + if (settlementSha256Hex(aad) !== envelope.aadHash) { + throw new Error("Settlement token AAD hash mismatch"); + } + const suppliedTag = base64UrlDecode(envelope.tag); + const tag = hmac(macKey, Buffer.concat([Buffer.from(aad, "utf8"), nonce, ciphertext])); + if (suppliedTag.length !== tag.length || !timingSafeEqual(suppliedTag, tag)) { + throw new Error("Settlement token authentication failed"); + } + const plaintext = JSON.parse(streamXor(ciphertext, encKey, nonce).toString("utf8")) as SettlementTokenPlaintext; + if (plaintext.transferId !== params.transferId) throw new Error("Settlement token transfer ID mismatch"); + if (settlementTokenCommitmentHash(plaintext) !== params.commitmentHash) { + throw new Error("Settlement token commitment mismatch"); + } + return plaintext; +} + +export function createOneTimeDecryptionToken(params: { + transferId: string; + leaseId: string; + crankerPubkey: string; + commitmentHash: string; + ttlMs?: number; + issuedAt?: string; + masterKey?: string | Buffer | Uint8Array; +}): OneTimeDecryptionToken { + const issuedAt = params.issuedAt ?? new Date().toISOString(); + const expiresAt = new Date(Date.parse(issuedAt) + (params.ttlMs ?? 10 * 60_000)).toISOString(); + const secret = base64UrlEncode(randomBytes(32)); + return { + id: base64UrlEncode(randomBytes(16)), + transferId: params.transferId, + leaseId: params.leaseId, + crankerPubkey: params.crankerPubkey, + commitmentHash: params.commitmentHash, + issuedAt, + expiresAt, + tokenHash: settlementSha256Hex(`${params.transferId}:${params.leaseId}:${params.crankerPubkey}:${params.commitmentHash}:${secret}`), + }; +} + +export function currentTsnEpoch(epochMs = 60 * 60 * 1000) { + return Math.floor(Date.now() / epochMs); +} diff --git a/tsn-sdk/tsconfig.tsbuildinfo b/tsn-sdk/tsconfig.tsbuildinfo index 1b93f27a..69c9b1a6 100644 --- a/tsn-sdk/tsconfig.tsbuildinfo +++ b/tsn-sdk/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/typescript/lib/lib.es2025.float16.d.ts","./node_modules/typescript/lib/lib.esnext.disposable.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./src/client.ts","./node_modules/@noble/hashes/esm/utils.d.ts","./node_modules/@noble/hashes/esm/_md.d.ts","./node_modules/@noble/hashes/esm/sha2.d.ts","./src/contracts.ts","./src/mempool.ts","./src/quote.ts","./node_modules/buffer/index.d.ts","./node_modules/@solana/web3.js/lib/index.d.ts","./src/program.ts","./src/send-estimate.ts","./src/settlement-economics.ts","./src/token-registry.ts","./src/payment-authorization.ts","./src/payment-authorization-server.ts","./src/payment-jobs.ts","./node_modules/@solana/spl-token/lib/types/actions/amounttouiamount.d.ts","./node_modules/@solana/spl-token/lib/types/actions/approve.d.ts","./node_modules/@solana/spl-token/lib/types/actions/approvechecked.d.ts","./node_modules/@solana/spl-token/lib/types/actions/burn.d.ts","./node_modules/@solana/spl-token/lib/types/actions/burnchecked.d.ts","./node_modules/@solana/spl-token/lib/types/actions/closeaccount.d.ts","./node_modules/@solana/spl-token/lib/types/actions/createaccount.d.ts","./node_modules/@solana/spl-token/lib/types/actions/createassociatedtokenaccount.d.ts","./node_modules/@solana/spl-token/lib/types/actions/createassociatedtokenaccountidempotent.d.ts","./node_modules/@solana/spl-token/lib/types/actions/createmint.d.ts","./node_modules/@solana/spl-token/lib/types/actions/createmultisig.d.ts","./node_modules/@solana/spl-token/lib/types/actions/createnativemint.d.ts","./node_modules/@solana/spl-token/lib/types/actions/createwrappednativeaccount.d.ts","./node_modules/@solana/spl-token/lib/types/actions/freezeaccount.d.ts","./node_modules/@solana/buffer-layout/lib/layout.d.ts","./node_modules/@solana/spl-token/lib/types/state/mint.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/extensiontype.d.ts","./node_modules/@solana/spl-token/lib/types/state/account.d.ts","./node_modules/@solana/spl-token/lib/types/actions/getorcreateassociatedtokenaccount.d.ts","./node_modules/@solana/spl-token/lib/types/actions/mintto.d.ts","./node_modules/@solana/spl-token/lib/types/actions/minttochecked.d.ts","./node_modules/@solana/spl-token/lib/types/actions/recovernested.d.ts","./node_modules/@solana/spl-token/lib/types/actions/revoke.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/types.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/setauthority.d.ts","./node_modules/@solana/spl-token/lib/types/actions/setauthority.d.ts","./node_modules/@solana/spl-token/lib/types/actions/syncnative.d.ts","./node_modules/@solana/spl-token/lib/types/actions/thawaccount.d.ts","./node_modules/@solana/spl-token/lib/types/actions/transfer.d.ts","./node_modules/@solana/spl-token/lib/types/actions/transferchecked.d.ts","./node_modules/@solana/spl-token/lib/types/actions/uiamounttoamount.d.ts","./node_modules/@solana/spl-token/lib/types/actions/index.d.ts","./node_modules/@solana/spl-token/lib/types/constants.d.ts","./node_modules/@solana/spl-token/lib/types/errors.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/accounttype.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/cpiguard/actions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/cpiguard/instructions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/cpiguard/state.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/cpiguard/index.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/defaultaccountstate/actions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/defaultaccountstate/instructions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/defaultaccountstate/state.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/defaultaccountstate/index.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/groupmemberpointer/instructions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/groupmemberpointer/state.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/groupmemberpointer/index.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/grouppointer/instructions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/grouppointer/state.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/grouppointer/index.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/immutableowner.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/interestbearingmint/actions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/interestbearingmint/instructions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/interestbearingmint/state.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/interestbearingmint/index.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/memotransfer/actions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/memotransfer/instructions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/memotransfer/state.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/memotransfer/index.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/metadatapointer/instructions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/metadatapointer/state.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/metadatapointer/index.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/scaleduiamount/actions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/scaleduiamount/instructions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/scaleduiamount/state.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/scaleduiamount/index.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/tokengroup/actions.d.ts","./node_modules/@solana/spl-token-group/lib/types/errors.d.ts","./node_modules/@solana/spl-token-group/lib/types/instruction.d.ts","./node_modules/@solana/codecs-core/dist/types/readonly-uint8array.d.ts","./node_modules/@solana/codecs-core/dist/types/codec.d.ts","./node_modules/@solana/codecs-core/dist/types/add-codec-sentinel.d.ts","./node_modules/@solana/codecs-core/dist/types/add-codec-size-prefix.d.ts","./node_modules/@solana/codecs-core/dist/types/assertions.d.ts","./node_modules/@solana/codecs-core/dist/types/bytes.d.ts","./node_modules/@solana/codecs-core/dist/types/combine-codec.d.ts","./node_modules/@solana/codecs-core/dist/types/fix-codec-size.d.ts","./node_modules/@solana/codecs-core/dist/types/offset-codec.d.ts","./node_modules/@solana/codecs-core/dist/types/pad-codec.d.ts","./node_modules/@solana/codecs-core/dist/types/resize-codec.d.ts","./node_modules/@solana/codecs-core/dist/types/reverse-codec.d.ts","./node_modules/@solana/codecs-core/dist/types/transform-codec.d.ts","./node_modules/@solana/codecs-core/dist/types/index.d.ts","./node_modules/@solana/codecs-numbers/dist/types/assertions.d.ts","./node_modules/@solana/codecs-numbers/dist/types/common.d.ts","./node_modules/@solana/codecs-numbers/dist/types/f32.d.ts","./node_modules/@solana/codecs-numbers/dist/types/f64.d.ts","./node_modules/@solana/codecs-numbers/dist/types/i128.d.ts","./node_modules/@solana/codecs-numbers/dist/types/i16.d.ts","./node_modules/@solana/codecs-numbers/dist/types/i32.d.ts","./node_modules/@solana/codecs-numbers/dist/types/i64.d.ts","./node_modules/@solana/codecs-numbers/dist/types/i8.d.ts","./node_modules/@solana/codecs-numbers/dist/types/short-u16.d.ts","./node_modules/@solana/codecs-numbers/dist/types/u128.d.ts","./node_modules/@solana/codecs-numbers/dist/types/u16.d.ts","./node_modules/@solana/codecs-numbers/dist/types/u32.d.ts","./node_modules/@solana/codecs-numbers/dist/types/u64.d.ts","./node_modules/@solana/codecs-numbers/dist/types/u8.d.ts","./node_modules/@solana/codecs-numbers/dist/types/index.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/array.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/assertions.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/bit-array.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/boolean.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/bytes.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/constant.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/utils.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/discriminated-union.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/enum-helpers.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/enum.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/hidden-prefix.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/hidden-suffix.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/map.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/nullable.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/set.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/struct.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/tuple.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/union.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/unit.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/index.d.ts","./node_modules/@solana/codecs-strings/dist/types/assertions.d.ts","./node_modules/@solana/codecs-strings/dist/types/base10.d.ts","./node_modules/@solana/codecs-strings/dist/types/base16.d.ts","./node_modules/@solana/codecs-strings/dist/types/base58.d.ts","./node_modules/@solana/codecs-strings/dist/types/base64.d.ts","./node_modules/@solana/codecs-strings/dist/types/basex.d.ts","./node_modules/@solana/codecs-strings/dist/types/basex-reslice.d.ts","./node_modules/@solana/codecs-strings/dist/types/null-characters.d.ts","./node_modules/@solana/codecs-strings/dist/types/utf8.d.ts","./node_modules/@solana/codecs-strings/dist/types/index.d.ts","./node_modules/@solana/options/dist/types/option.d.ts","./node_modules/@solana/options/dist/types/option-codec.d.ts","./node_modules/@solana/options/dist/types/unwrap-option.d.ts","./node_modules/@solana/options/dist/types/unwrap-option-recursively.d.ts","./node_modules/@solana/options/dist/types/index.d.ts","./node_modules/@solana/codecs/dist/types/index.d.ts","./node_modules/@solana/spl-token-group/lib/types/state/tokengroup.d.ts","./node_modules/@solana/spl-token-group/lib/types/state/tokengroupmember.d.ts","./node_modules/@solana/spl-token-group/lib/types/state/index.d.ts","./node_modules/@solana/spl-token-group/lib/types/index.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/tokengroup/state.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/tokengroup/index.d.ts","./node_modules/@solana/spl-token-metadata/lib/types/errors.d.ts","./node_modules/@solana/spl-token-metadata/lib/types/field.d.ts","./node_modules/@solana/spl-token-metadata/lib/types/instruction.d.ts","./node_modules/@solana/spl-token-metadata/lib/types/state.d.ts","./node_modules/@solana/spl-token-metadata/lib/types/index.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/tokenmetadata/actions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/tokenmetadata/state.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/tokenmetadata/index.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/mintcloseauthority.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/nontransferable.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/transferfee/actions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/transferfee/instructions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/transferfee/state.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/transferfee/index.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/permanentdelegate.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/transferhook/actions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/transferhook/instructions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/transferhook/seeds.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/transferhook/state.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/transferhook/pubkeydata.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/transferhook/index.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/pausable/actions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/pausable/instructions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/pausable/state.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/pausable/index.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/index.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/associatedtokenaccount.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/amounttouiamount.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/approve.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/approvechecked.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/burn.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/burnchecked.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/closeaccount.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/freezeaccount.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/initializeaccount.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/initializeaccount2.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/initializeaccount3.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/initializemint.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/initializemint2.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/initializemultisig.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/mintto.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/minttochecked.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/revoke.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/syncnative.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/thawaccount.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/transfer.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/transferchecked.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/uiamounttoamount.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/decode.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/initializemultisig2.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/initializeimmutableowner.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/initializemintcloseauthority.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/reallocate.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/createnativemint.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/initializenontransferablemint.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/initializepermanentdelegate.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/index.d.ts","./node_modules/@solana/spl-token/lib/types/state/multisig.d.ts","./node_modules/@solana/spl-token/lib/types/state/index.d.ts","./node_modules/@solana/spl-token/lib/types/index.d.ts","./src/sponsored-settlement.ts","./src/tins.ts","./src/blockchain/solana-core.ts","./node_modules/eventemitter3/index.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/idl.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/program/context.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/program/common.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/utils/rpc.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/provider.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/nodewallet.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/error.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/program/namespace/account.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/program/accounts-resolver.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/program/namespace/instruction.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/program/namespace/transaction.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/program/namespace/rpc.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/program/namespace/simulate.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/program/namespace/views.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/program/namespace/methods.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/program/namespace/types.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/program/event.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/coder/borsh/accounts.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/coder/borsh/event.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/coder/borsh/types.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/coder/borsh/index.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/coder/system/instruction.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/coder/system/accounts.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/coder/system/events.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/coder/system/types.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/coder/system/index.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/coder/index.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/coder/borsh/instruction.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/utils/sha256.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/utils/pubkey.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/utils/bytes/hex.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/utils/bytes/utf8.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/utils/bytes/bs58.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/utils/bytes/base64.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/utils/bytes/index.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/utils/token.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/utils/features.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/utils/registry.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/utils/index.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/program/namespace/index.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/program/index.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/native/system.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/native/index.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/index.d.ts","./src/lib/logger.ts","./src/blockchain/solana-tsn.ts","./src/index.ts","./src/server.ts","./node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/@types/node/globals.typedarray.d.ts","./node_modules/@types/node/buffer.buffer.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/web-globals/abortcontroller.d.ts","./node_modules/@types/node/web-globals/blob.d.ts","./node_modules/@types/node/web-globals/console.d.ts","./node_modules/@types/node/web-globals/crypto.d.ts","./node_modules/@types/node/web-globals/domexception.d.ts","./node_modules/@types/node/web-globals/encoding.d.ts","./node_modules/@types/node/web-globals/events.d.ts","./node_modules/undici-types/utility.d.ts","./node_modules/undici-types/header.d.ts","./node_modules/undici-types/readable.d.ts","./node_modules/undici-types/fetch.d.ts","./node_modules/undici-types/formdata.d.ts","./node_modules/undici-types/connector.d.ts","./node_modules/undici-types/client-stats.d.ts","./node_modules/undici-types/client.d.ts","./node_modules/undici-types/errors.d.ts","./node_modules/undici-types/dispatcher.d.ts","./node_modules/undici-types/global-dispatcher.d.ts","./node_modules/undici-types/global-origin.d.ts","./node_modules/undici-types/pool-stats.d.ts","./node_modules/undici-types/pool.d.ts","./node_modules/undici-types/handlers.d.ts","./node_modules/undici-types/balanced-pool.d.ts","./node_modules/undici-types/round-robin-pool.d.ts","./node_modules/undici-types/h2c-client.d.ts","./node_modules/undici-types/agent.d.ts","./node_modules/undici-types/mock-interceptor.d.ts","./node_modules/undici-types/mock-call-history.d.ts","./node_modules/undici-types/mock-agent.d.ts","./node_modules/undici-types/mock-client.d.ts","./node_modules/undici-types/mock-pool.d.ts","./node_modules/undici-types/snapshot-agent.d.ts","./node_modules/undici-types/mock-errors.d.ts","./node_modules/undici-types/proxy-agent.d.ts","./node_modules/undici-types/socks5-proxy-agent.d.ts","./node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/undici-types/retry-handler.d.ts","./node_modules/undici-types/retry-agent.d.ts","./node_modules/undici-types/api.d.ts","./node_modules/undici-types/cache-interceptor.d.ts","./node_modules/undici-types/interceptors.d.ts","./node_modules/undici-types/util.d.ts","./node_modules/undici-types/cookies.d.ts","./node_modules/undici-types/patch.d.ts","./node_modules/undici-types/websocket.d.ts","./node_modules/undici-types/eventsource.d.ts","./node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/undici-types/content-type.d.ts","./node_modules/undici-types/cache.d.ts","./node_modules/undici-types/index.d.ts","./node_modules/@types/node/web-globals/fetch.d.ts","./node_modules/@types/node/web-globals/importmeta.d.ts","./node_modules/@types/node/web-globals/messaging.d.ts","./node_modules/@types/node/web-globals/navigator.d.ts","./node_modules/@types/node/web-globals/performance.d.ts","./node_modules/@types/node/web-globals/storage.d.ts","./node_modules/@types/node/web-globals/streams.d.ts","./node_modules/@types/node/web-globals/timers.d.ts","./node_modules/@types/node/web-globals/url.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/assert/strict.d.ts","./node_modules/@types/node/async_hooks.d.ts","./node_modules/@types/node/buffer.d.ts","./node_modules/@types/node/child_process.d.ts","./node_modules/@types/node/cluster.d.ts","./node_modules/@types/node/console.d.ts","./node_modules/@types/node/constants.d.ts","./node_modules/@types/node/crypto.d.ts","./node_modules/@types/node/dgram.d.ts","./node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/@types/node/dns.d.ts","./node_modules/@types/node/dns/promises.d.ts","./node_modules/@types/node/domain.d.ts","./node_modules/@types/node/events.d.ts","./node_modules/@types/node/fs.d.ts","./node_modules/@types/node/fs/promises.d.ts","./node_modules/@types/node/http.d.ts","./node_modules/@types/node/http2.d.ts","./node_modules/@types/node/https.d.ts","./node_modules/@types/node/inspector.d.ts","./node_modules/@types/node/inspector.generated.d.ts","./node_modules/@types/node/inspector/promises.d.ts","./node_modules/@types/node/module.d.ts","./node_modules/@types/node/net.d.ts","./node_modules/@types/node/os.d.ts","./node_modules/@types/node/path.d.ts","./node_modules/@types/node/path/posix.d.ts","./node_modules/@types/node/path/win32.d.ts","./node_modules/@types/node/perf_hooks.d.ts","./node_modules/@types/node/process.d.ts","./node_modules/@types/node/punycode.d.ts","./node_modules/@types/node/querystring.d.ts","./node_modules/@types/node/quic.d.ts","./node_modules/@types/node/readline.d.ts","./node_modules/@types/node/readline/promises.d.ts","./node_modules/@types/node/repl.d.ts","./node_modules/@types/node/sea.d.ts","./node_modules/@types/node/sqlite.d.ts","./node_modules/@types/node/stream.d.ts","./node_modules/@types/node/stream/consumers.d.ts","./node_modules/@types/node/stream/iter.d.ts","./node_modules/@types/node/stream/promises.d.ts","./node_modules/@types/node/stream/web.d.ts","./node_modules/@types/node/string_decoder.d.ts","./node_modules/@types/node/test.d.ts","./node_modules/@types/node/test/reporters.d.ts","./node_modules/@types/node/timers.d.ts","./node_modules/@types/node/timers/promises.d.ts","./node_modules/@types/node/tls.d.ts","./node_modules/@types/node/trace_events.d.ts","./node_modules/@types/node/tty.d.ts","./node_modules/@types/node/url.d.ts","./node_modules/@types/node/util.d.ts","./node_modules/@types/node/util/types.d.ts","./node_modules/@types/node/v8.d.ts","./node_modules/@types/node/vm.d.ts","./node_modules/@types/node/wasi.d.ts","./node_modules/@types/node/worker_threads.d.ts","./node_modules/@types/node/zlib.d.ts","./node_modules/@types/node/zlib/iter.d.ts","./node_modules/@types/node/index.d.ts"],"fileIdsList":[[280,306,330,394,402,406,409,411,412,413,425],[280,297,298,299,306,307,330,394,402,406,409,411,412,413,425],[68,280,306,330,394,402,406,409,411,412,413,425],[280,296,300,305,330,394,402,406,409,411,412,413,425],[280,296,306,330,394,402,406,409,411,412,413,425],[280,301,302,303,304,306,330,394,402,406,409,411,412,413,425],[68,330,394,402,406,409,411,412,413,425],[68,280,284,285,286,288,306,307,318,320,322,330,394,402,406,409,411,412,413,425],[321,323,330,394,402,406,409,411,412,413,425],[68,284,305,320,330,394,402,406,409,411,412,413,425],[68,284,330,394,402,406,409,411,412,413,425],[68,280,284,287,294,295,330,394,402,406,409,411,412,413,425],[68,279,280,281,330,394,402,406,409,411,412,413,425],[68,280,282,330,394,402,406,409,411,412,413,425],[68,280,284,295,306,330,394,402,406,409,411,412,413,425],[68,280,281,282,284,288,296,306,319,330,394,402,406,409,411,412,413,425],[68,279,280,282,284,295,306,330,394,402,406,409,411,412,413,425],[68,280,284,287,288,289,290,291,292,293,294,295,306,330,394,402,406,409,411,412,413,425],[68,280,281,295,330,394,402,406,409,411,412,413,425],[68,280,281,282,284,287,288,289,290,291,292,293,295,330,394,402,406,409,411,412,413,425],[68,280,284,290,295,330,394,402,406,409,411,412,413,425],[68,280,284,290,295,296,306,330,394,402,406,409,411,412,413,425],[68,280,289,295,330,394,402,406,409,411,412,413,425],[68,280,281,294,330,394,402,406,409,411,412,413,425],[68,280,292,295,330,394,402,406,409,411,412,413,425],[68,283,330,394,402,406,409,411,412,413,425],[330,394,402,406,409,411,412,413,425],[310,311,312,313,330,394,402,406,409,411,412,413,425],[283,308,309,314,315,316,317,330,394,402,406,409,411,412,413,425],[68,282,284,330,394,402,406,409,411,412,413,425],[61,330,394,402,406,409,411,412,413,425],[61,62,330,394,402,406,409,411,412,413,425],[144,145,330,394,402,406,409,411,412,413,425],[145,330,394,402,406,409,411,412,413,425],[144,330,394,402,406,409,411,412,413,425],[144,145,146,147,148,149,150,151,152,153,154,155,156,330,394,402,406,409,411,412,413,425],[157,173,330,394,402,406,409,411,412,413,425],[157,330,394,402,406,409,411,412,413,425],[157,173,180,330,394,402,406,409,411,412,413,425],[157,173,182,330,394,402,406,409,411,412,413,425],[174,175,176,177,178,179,181,183,184,185,186,187,188,189,190,191,192,330,394,402,406,409,411,412,413,425],[157,173,174,330,394,402,406,409,411,412,413,425],[157,180,330,394,402,406,409,411,412,413,425],[157,159,330,394,402,406,409,411,412,413,425],[158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,330,394,402,406,409,411,412,413,425],[194,195,196,197,198,199,200,201,202,330,394,402,406,409,411,412,413,425],[157,173,193,203,208,330,394,402,406,409,411,412,413,425],[204,205,206,207,330,394,402,406,409,411,412,413,425],[157,173,204,330,394,402,406,409,411,412,413,425],[204,330,394,402,406,409,411,412,413,425],[142,143,212,330,394,402,406,409,411,412,413,425],[210,211,330,394,402,406,409,411,412,413,425],[68,209,330,394,402,406,409,411,412,413,425],[209,330,394,402,406,409,411,412,413,425],[216,217,218,219,330,394,402,406,409,411,412,413,425],[68,217,330,394,402,406,409,411,412,413,425],[68,93,330,394,402,406,409,411,412,413,425],[76,77,78,79,80,81,82,83,84,85,86,87,88,89,94,95,96,97,98,101,102,103,104,105,106,330,394,402,406,409,411,412,413,425],[68,100,330,394,402,406,409,411,412,413,425],[111,112,113,330,394,402,406,409,411,412,413,425],[68,90,99,330,394,402,406,409,411,412,413,425],[90,93,330,394,402,406,409,411,412,413,425],[115,116,117,330,394,402,406,409,411,412,413,425],[68,90,93,99,330,394,402,406,409,411,412,413,425],[90,91,93,330,394,402,406,409,411,412,413,425],[68,91,330,394,402,406,409,411,412,413,425],[119,120,330,394,402,406,409,411,412,413,425],[68,90,91,330,394,402,406,409,411,412,413,425],[122,123,330,394,402,406,409,411,412,413,425],[92,110,114,118,121,124,125,129,133,136,140,215,223,224,225,229,230,236,240,330,394,402,406,409,411,412,413,425],[126,127,128,330,394,402,406,409,411,412,413,425],[130,131,132,330,394,402,406,409,411,412,413,425],[134,135,330,394,402,406,409,411,412,413,425],[237,238,239,330,394,402,406,409,411,412,413,425],[68,90,91,93,330,394,402,406,409,411,412,413,425],[137,138,139,330,394,402,406,409,411,412,413,425],[141,214,330,394,402,406,409,411,412,413,425],[91,213,330,394,402,406,409,411,412,413,425],[68,220,330,394,402,406,409,411,412,413,425],[221,222,330,394,402,406,409,411,412,413,425],[226,227,228,330,394,402,406,409,411,412,413,425],[231,232,233,234,235,330,394,402,406,409,411,412,413,425],[107,108,109,241,272,274,330,394,402,406,409,411,412,413,425],[68,100,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,330,394,402,406,409,411,412,413,425],[99,100,213,220,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,330,394,402,406,409,411,412,413,425],[68,92,99,330,394,402,406,409,411,412,413,425],[68,99,330,394,402,406,409,411,412,413,425],[68,90,92,330,394,402,406,409,411,412,413,425],[91,93,273,330,394,402,406,409,411,412,413,425],[68,90,330,394,402,406,409,411,412,413,425],[330,394,402,406,408,409,410,411,412,413,425],[330,391,392,394,402,406,409,411,412,413,425],[330,393,394,402,406,409,411,412,413,425],[394,402,406,409,411,412,413,425],[330,394,402,406,409,411,412,413,425,434],[330,394,395,400,402,405,406,409,411,412,413,415,425,430,443],[330,394,395,396,402,405,406,409,411,412,413,425],[330,394,397,402,406,409,411,412,413,425,444],[330,394,398,399,402,406,409,411,412,413,416,425],[330,394,399,402,406,409,411,412,413,425,430,440],[330,394,400,402,405,406,409,411,412,413,415,425],[330,393,394,401,402,406,409,411,412,413,425],[330,394,402,403,406,409,411,412,413,425],[330,394,402,404,405,406,409,411,412,413,425],[330,393,394,402,405,406,409,411,412,413,425],[330,394,402,405,406,407,409,411,412,413,425,430,443],[330,394,402,405,406,407,409,411,412,413,425,430,432,434],[330,381,394,402,405,406,408,409,411,412,413,415,425,430,443],[330,394,402,405,406,408,409,411,412,413,415,425,430,440,443],[330,394,402,406,408,409,410,411,412,413,425,430,440,443],[328,329,330,331,332,333,334,335,336,337,338,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451],[330,394,402,405,406,409,411,412,413,425],[330,394,402,406,409,411,413,425],[330,394,402,406,409,411,412,413,414,425,443],[330,394,402,405,406,409,411,412,413,415,425,430],[330,394,402,406,409,411,412,413,416,425],[330,394,402,406,409,411,412,413,417,425],[330,394,402,405,406,409,411,412,413,420,425],[330,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450],[330,394,402,406,409,411,412,413,422,425],[330,394,402,406,409,411,412,413,423,425],[330,394,399,402,406,409,411,412,413,415,425,434],[330,394,402,405,406,409,411,412,413,425,426],[330,394,402,406,409,411,412,413,425,427,444,447],[330,394,402,405,406,409,411,412,413,425,430,433,434],[330,394,402,406,409,411,412,413,425,431,434],[330,394,402,406,409,411,412,413,425,432],[330,394,402,406,409,411,412,413,425,434,444],[330,394,402,406,409,411,412,413,425,435],[330,391,394,402,406,409,411,412,413,425,430,437,443],[330,394,402,406,409,411,412,413,425,430,436],[330,394,402,405,406,409,411,412,413,425,438,439],[330,394,402,406,409,411,412,413,425,438,439],[330,394,399,402,406,409,411,412,413,415,425,430,440],[330,394,402,406,409,411,412,413,425,441],[330,394,402,406,409,411,412,413,415,425,442],[330,394,402,406,408,409,411,412,413,423,425,443],[330,394,402,406,409,411,412,413,425,444,445],[330,394,399,402,406,409,411,412,413,425,445],[330,394,402,406,409,411,412,413,425,430,446],[330,394,402,406,409,411,412,413,414,425,447],[330,394,402,406,409,411,412,413,425,448],[330,394,397,402,406,409,411,412,413,425],[330,394,399,402,406,409,411,412,413,425],[330,394,402,406,409,411,412,413,425,444],[330,381,394,402,406,409,411,412,413,425],[330,394,402,406,409,411,412,413,425,443],[330,394,402,406,409,411,412,413,425,449],[330,394,402,406,409,411,412,413,420,425],[330,394,402,406,409,411,412,413,425,439],[330,381,394,402,405,406,407,409,411,412,413,420,425,430,434,443,446,447,449],[330,394,402,406,409,411,412,413,425,430,450],[330,394,402,406,409,411,412,413,425,432,451],[330,345,348,351,352,394,402,406,409,411,412,413,425,443],[330,348,394,402,406,409,411,412,413,425,430,443],[330,348,352,394,402,406,409,411,412,413,425,443],[330,394,402,406,409,411,412,413,425,430],[330,342,394,402,406,409,411,412,413,425],[330,346,394,402,406,409,411,412,413,425],[330,344,345,348,394,402,406,409,411,412,413,425,443],[330,394,402,406,409,411,412,413,415,425,440],[330,394,402,406,409,411,412,413,425,452],[330,342,394,402,406,409,411,412,413,425,452],[330,344,348,394,402,406,409,411,412,413,415,425,443],[330,339,340,341,343,347,394,402,405,406,409,411,412,413,425,430,443],[330,348,357,365,394,402,406,409,411,412,413,425],[330,340,346,394,402,406,409,411,412,413,425],[330,348,375,376,394,402,406,409,411,412,413,425],[330,340,343,348,394,402,406,409,411,412,413,425,434,443,452],[330,348,394,402,406,409,411,412,413,425],[330,344,348,394,402,406,409,411,412,413,425,443],[330,339,394,402,406,409,411,412,413,425],[330,342,343,344,346,347,348,349,350,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,376,377,378,379,380,394,402,406,409,411,412,413,425],[330,348,368,371,394,402,406,409,411,412,413,425],[330,348,357,358,359,394,402,406,409,411,412,413,425],[330,346,348,358,360,394,402,406,409,411,412,413,425],[330,347,394,402,406,409,411,412,413,425],[330,340,342,348,394,402,406,409,411,412,413,425],[330,348,352,358,360,394,402,406,409,411,412,413,425],[330,352,394,402,406,409,411,412,413,425],[330,346,348,351,394,402,406,409,411,412,413,425,443],[330,340,344,348,357,394,402,406,409,411,412,413,425],[330,348,368,394,402,406,409,411,412,413,425],[330,360,394,402,406,409,411,412,413,425],[330,340,344,348,352,394,402,406,409,411,412,413,425],[330,342,348,375,394,402,406,409,411,412,413,425,434,449,452],[68,69,275,330,394,399,402,406,409,411,412,413,425],[68,69,275,278,323,324,330,394,399,402,406,409,411,412,413,425],[61,63,330,394,402,406,409,411,412,413,425],[60,64,65,66,69,70,71,72,73,74,75,276,277,278,325,330,394,402,406,409,411,412,413,425],[60,64,330,394,399,402,406,407,409,411,412,413,417,425],[68,330,394,399,402,406,409,411,412,413,425],[60,64,330,394,402,406,409,411,412,413,425],[64,65,73,74,330,394,402,406,409,411,412,413,425],[66,68,69,330,394,402,406,409,411,412,413,425],[65,330,394,402,406,408,409,411,412,413,425],[61,63,68,69,275,330,394,402,406,409,411,412,413,425],[63,68,330,394,402,406,409,411,412,413,425]],"fileInfos":[{"version":"bcd24271a113971ba9eb71ff8cb01bc6b0f872a85c23fdbe5d93065b375933cd","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f88bedbeb09c6f5a6645cb24c7c55f1aa22d19ae96c8e6959cbd8b85a707bc6","impliedFormat":1},{"version":"7fe93b39b810eadd916be8db880dd7f0f7012a5cc6ffb62de8f62a2117fa6f1f","impliedFormat":1},{"version":"bb0074cc08b84a2374af33d8bf044b80851ccc9e719a5e202eacf40db2c31600","impliedFormat":1},{"version":"1a7daebe4f45fb03d9ec53d60008fbf9ac45a697fdc89e4ce218bc94b94f94d6","impliedFormat":1},{"version":"f94b133a3cb14a288803be545ac2683e0d0ff6661bcd37e31aaaec54fc382aed","impliedFormat":1},{"version":"f59d0650799f8782fd74cf73c19223730c6d1b9198671b1c5b3a38e1188b5953","impliedFormat":1},{"version":"8a15b4607d9a499e2dbeed9ec0d3c0d7372c850b2d5f1fb259e8f6d41d468a84","impliedFormat":1},{"version":"26e0fe14baee4e127f4365d1ae0b276f400562e45e19e35fd2d4c296684715e6","impliedFormat":1},{"version":"eadcffda2aa84802c73938e589b9e58248d74c59cb7fcbca6474e3435ac15504","affectsGlobalScope":true,"impliedFormat":1},{"version":"105ba8ff7ba746404fe1a2e189d1d3d2e0eb29a08c18dded791af02f29fb4711","affectsGlobalScope":true,"impliedFormat":1},{"version":"00343ca5b2e3d48fa5df1db6e32ea2a59afab09590274a6cccb1dbae82e60c7c","affectsGlobalScope":true,"impliedFormat":1},{"version":"ebd9f816d4002697cb2864bea1f0b70a103124e18a8cd9645eeccc09bdf80ab4","affectsGlobalScope":true,"impliedFormat":1},{"version":"2c1afac30a01772cd2a9a298a7ce7706b5892e447bb46bdbeef720f7b5da77ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"7b0225f483e4fa685625ebe43dd584bb7973bbd84e66a6ba7bbe175ee1048b4f","affectsGlobalScope":true,"impliedFormat":1},{"version":"c0a4b8ac6ce74679c1da2b3795296f5896e31c38e888469a8e0f99dc3305de60","affectsGlobalScope":true,"impliedFormat":1},{"version":"3084a7b5f569088e0146533a00830e206565de65cae2239509168b11434cd84f","affectsGlobalScope":true,"impliedFormat":1},{"version":"c5079c53f0f141a0698faa903e76cb41cd664e3efb01cc17a5c46ec2eb0bef42","affectsGlobalScope":true,"impliedFormat":1},{"version":"32cafbc484dea6b0ab62cf8473182bbcb23020d70845b406f80b7526f38ae862","affectsGlobalScope":true,"impliedFormat":1},{"version":"fca4cdcb6d6c5ef18a869003d02c9f0fd95df8cfaf6eb431cd3376bc034cad36","affectsGlobalScope":true,"impliedFormat":1},{"version":"b93ec88115de9a9dc1b602291b85baf825c85666bf25985cc5f698073892b467","affectsGlobalScope":true,"impliedFormat":1},{"version":"f5c06dcc3fe849fcb297c247865a161f995cc29de7aa823afdd75aaaddc1419b","affectsGlobalScope":true,"impliedFormat":1},{"version":"b77e16112127a4b169ef0b8c3a4d730edf459c5f25fe52d5e436a6919206c4d7","affectsGlobalScope":true,"impliedFormat":1},{"version":"fbffd9337146eff822c7c00acbb78b01ea7ea23987f6c961eba689349e744f8c","affectsGlobalScope":true,"impliedFormat":1},{"version":"a995c0e49b721312f74fdfb89e4ba29bd9824c770bbb4021d74d2bf560e4c6bd","affectsGlobalScope":true,"impliedFormat":1},{"version":"c7b3542146734342e440a84b213384bfa188835537ddbda50d30766f0593aff9","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce6180fa19b1cccd07ee7f7dbb9a367ac19c0ed160573e4686425060b6df7f57","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f02e2476bccb9dbe21280d6090f0df17d2f66b74711489415a8aa4df73c9675","affectsGlobalScope":true,"impliedFormat":1},{"version":"45e3ab34c1c013c8ab2dc1ba4c80c780744b13b5676800ae2e3be27ae862c40c","affectsGlobalScope":true,"impliedFormat":1},{"version":"805c86f6cca8d7702a62a844856dbaa2a3fd2abef0536e65d48732441dde5b5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"e42e397f1a5a77994f0185fd1466520691456c772d06bf843e5084ceb879a0ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"f4c2b41f90c95b1c532ecc874bd3c111865793b23aebcc1c3cbbabcd5d76ffb0","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab26191cfad5b66afa11b8bf935ef1cd88fabfcb28d30b2dfa6fad877d050332","affectsGlobalScope":true,"impliedFormat":1},{"version":"2088bc26531e38fb05eedac2951480db5309f6be3fa4a08d2221abb0f5b4200d","affectsGlobalScope":true,"impliedFormat":1},{"version":"cb9d366c425fea79716a8fb3af0d78e6b22ebbab3bd64d25063b42dc9f531c1e","affectsGlobalScope":true,"impliedFormat":1},{"version":"500934a8089c26d57ebdb688fc9757389bb6207a3c8f0674d68efa900d2abb34","affectsGlobalScope":true,"impliedFormat":1},{"version":"689da16f46e647cef0d64b0def88910e818a5877ca5379ede156ca3afb780ac3","affectsGlobalScope":true,"impliedFormat":1},{"version":"bc21cc8b6fee4f4c2440d08035b7ea3c06b3511314c8bab6bef7a92de58a2593","affectsGlobalScope":true,"impliedFormat":1},{"version":"7ca53d13d2957003abb47922a71866ba7cb2068f8d154877c596d63c359fed25","affectsGlobalScope":true,"impliedFormat":1},{"version":"54725f8c4df3d900cb4dac84b64689ce29548da0b4e9b7c2de61d41c79293611","affectsGlobalScope":true,"impliedFormat":1},{"version":"e5594bc3076ac29e6c1ebda77939bc4c8833de72f654b6e376862c0473199323","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f3eb332c2d73e729f3364fcc0c2b375e72a121e8157d25a82d67a138c83a95c","affectsGlobalScope":true,"impliedFormat":1},{"version":"6f4427f9642ce8d500970e4e69d1397f64072ab73b97e476b4002a646ac743b1","affectsGlobalScope":true,"impliedFormat":1},{"version":"48915f327cd1dea4d7bd358d9dc7732f58f9e1626a29cc0c05c8c692419d9bb7","affectsGlobalScope":true,"impliedFormat":1},{"version":"b7bf9377723203b5a6a4b920164df22d56a43f593269ba6ae1fdc97774b68855","affectsGlobalScope":true,"impliedFormat":1},{"version":"db9709688f82c9e5f65a119c64d835f906efe5f559d08b11642d56eb85b79357","affectsGlobalScope":true,"impliedFormat":1},{"version":"4b25b8c874acd1a4cf8444c3617e037d444d19080ac9f634b405583fd10ce1f7","affectsGlobalScope":true,"impliedFormat":1},{"version":"37be57d7c90cf1f8112ee2636a068d8fd181289f82b744160ec56a7dc158a9f5","affectsGlobalScope":true,"impliedFormat":1},{"version":"a917a49ac94cd26b754ab84e113369a75d1a47a710661d7cd25e961cc797065f","affectsGlobalScope":true,"impliedFormat":1},{"version":"6d3261badeb7843d157ef3e6f5d1427d0eeb0af0cf9df84a62cfd29fd47ac86e","affectsGlobalScope":true,"impliedFormat":1},{"version":"195daca651dde22f2167ac0d0a05e215308119a3100f5e6268e8317d05a92526","affectsGlobalScope":true,"impliedFormat":1},{"version":"8b11e4285cd2bb164a4dc09248bdec69e9842517db4ca47c1ba913011e44ff2f","affectsGlobalScope":true,"impliedFormat":1},{"version":"0508571a52475e245b02bc50fa1394065a0a3d05277fbf5120c3784b85651799","affectsGlobalScope":true,"impliedFormat":1},{"version":"8f9af488f510c3015af3cc8c267a9e9d96c4dd38a1fdff0e11dc5a544711415b","affectsGlobalScope":true,"impliedFormat":1},{"version":"fc611fea8d30ea72c6bbfb599c9b4d393ce22e2f5bfef2172534781e7d138104","affectsGlobalScope":true,"impliedFormat":1},{"version":"f128dae7c44d8f35ee42e0a437000a57c9f06cc04f8b4fb42eebf44954d53dc8","affectsGlobalScope":true,"impliedFormat":1},{"version":"1ecb8e347cb6b2a8927c09b86263663289418df375f5e68e11a0ae683776978f","affectsGlobalScope":true,"impliedFormat":1},{"version":"1ce14b81c5cc821994aa8ec1d42b220dd41b27fcc06373bce3958af7421b77d4","affectsGlobalScope":true,"impliedFormat":1},{"version":"b3a048b3e9302ef9a34ef4ebb9aecfb28b66abb3bce577206a79fee559c230da","affectsGlobalScope":true,"impliedFormat":1},{"version":"f942fd040f6919f79dc8cb75da236c1dc5153bbf00ffedaaf5c0faa94a72e3e1","signature":"6a630f0117cb72926c4655069e47797d5b459f96bc3be828acb40b83a404b3de"},{"version":"b0bf8f866d3c05dce6c2778455252391bbc3fa0e8c1675e78dcee8fab2e1dd96","impliedFormat":99},{"version":"675058f412cecd4e2c028e1a74aa34d5510ab03ed78dae712437890bb0aba6ba","impliedFormat":99},{"version":"cf6dc97686cc424e560bc9938f79964cccecd270ad144ac0ba85f2d8caa1115d","impliedFormat":99},{"version":"1e3d9731376d02ba36851f4555b2ae781b113a553f2649ea0c93ee4663b86205","signature":"95a345e9b0a1dff39d94b77d0d119fd69c013a05f20de07ad900f2145bad92fc"},{"version":"c56d80b5e3efc0f965931b459c08069ec1a86c08af75a72a37abb1f0c1de7b90","signature":"2ad2d4c382728e9de6b72f6c78ce7ac57ed5de0a95108de4d5f339756639063a"},{"version":"f5a43db13bb0d8e8976341983d01116e046303c21de6a405953de8be75ad5610","signature":"eda265ad66f026b17160885823f1b963303816eec58e2c61a13fa01daba2d52d"},{"version":"4967529644e391115ca5592184d4b63980569adf60ee685f968fd59ab1557188","impliedFormat":1},{"version":"354648085514ffc13bde79849f02ee20496ec3807b751e07aed2105e6848396d","impliedFormat":1},{"version":"c8995f28ef7d5d3c11d51ef9880597b9dfddb50dc4c119fc58f3176034643461","signature":"102cf8b4beee187a48c4a19b40ef322afab3814c2ada7188bbc153213befb930"},{"version":"0956d07ee7ba33ffe94ca03c68a509d2653f1c7d17e66db4fc01c2426ca080e2","signature":"cd0f2b04c58a6ce8b6ed71438602561cdbfd31e58167d2397825d5e3a72069aa"},{"version":"f5df3fe9ca26bd9dd9e33449a01dcbcdad5d973943f0c10d48fe95d83092cc7c","signature":"162d529e90c52a13fd59d4ec83fdd9503c6ab1783a946eac0ac40ba95a33df63"},{"version":"b22940c33e79cfc4a4a651072f542106440b63b82c3836031188fb22865de05a","signature":"2f18fcffde4f3e540f8be12e6f1e38fdba4c5937bf46dc72359e9e46ae18fa5a"},{"version":"022bb80a7f30653ba6aca185762c1f23bd040bd6812588dbf62c7a3b5d35f324","signature":"6680d1c9c0a51fe16251ddbf07a93df61cb212bc4194e8edb6bd6c63d2f26837"},{"version":"e361b3766556e8ffb41e294eb5baf322d042152a70a363923682f788969d9480","signature":"2d0b66eda93ab76ad7fba6f4ae14715e1b2bb1d2127326e74c577800917f0b0c"},{"version":"bf3d17f1381f402fb6a72bc123b3f723ffed4a26ad4bcbcd5a560bb339a7994e","signature":"f10a2db30fcd5702e911a3b260d575785f03fa27ee8c1343fbbe42f2dd271cd5"},{"version":"e3ba886aaac6533cc4b9b19846d4f950420997b9a61beaf907d15ea3290a9c40","impliedFormat":99},{"version":"9da3be1581103e0acf5f08ebc5ed24460322dfafb1ac69da014d5dc4a483a800","impliedFormat":99},{"version":"da6886dca4ee769cb1fa40757d9172a83368e361be7bef76ec3cf07e4dc5f8a9","impliedFormat":99},{"version":"8685c3d0ddfc9dc4e46a2004c2f2ec4d87efeb5f0ad6a53d48c0582d3ffe0761","impliedFormat":99},{"version":"aad2e723639c5d10e757430433edc63bc47c80a2771d5f29a47d72efe5ccbc16","impliedFormat":99},{"version":"9bc5e15c3e8d531d2bd30df972010e70666bd6bd4de57ea73c8a54c256f201cf","impliedFormat":99},{"version":"366b4a710a821e9d12a8ba59976cb619255351b1ed46ff2a7cb59e52e3c5efd9","impliedFormat":99},{"version":"2eab9bd13de1418725bffeb914252923dfe7737aed548da636d1f83fe86d90ce","impliedFormat":99},{"version":"52e27ca19a14551040f2d416cc461ffb66d1e580614c9ded60349457aa8dc32f","impliedFormat":99},{"version":"ff95ffb2c3a0c528ecdef1d4f9d0583b053c7b5b14fad0484ca24a10f3d803c0","impliedFormat":99},{"version":"efbbcd99bc7d919c901830d380d550f09d54fed91ba10d7c01fd637100523e56","impliedFormat":99},{"version":"4ff9e90fd09f201e27e286c42b2e39947a4dbffebe8b1e363c19dc24e7de0cbc","impliedFormat":99},{"version":"c908d519afbcec2451ef3236e1e60ff0864a20008bb362b1dc65faae0d4a209f","impliedFormat":99},{"version":"2f0b1a054dc679eff33ea5d78f38fb52be3a2828484a089345a5922ca015d4ff","impliedFormat":99},{"version":"92ad95e6220ab829c8f5cfca9be43e26e041a2922cde6e998782030d41c49963","impliedFormat":1},{"version":"4efb7b1f86f666d3ccc0980950fbfd47433853ba0279335ac3a448de52e6ff0a","impliedFormat":99},{"version":"4c3d23ed1f0ae934ce1c70036bb86432853203a3c9bef65b0508cabe78bc8471","impliedFormat":99},{"version":"bce555ca7b622803e6d02fd5b329b8f9a1d210ce50170f7d9dc159850ee12289","impliedFormat":99},{"version":"def16e36d2c06f660bfd5b1475124076951f96be19432806b75fbb14fcf3585b","impliedFormat":99},{"version":"43ee8c4a935c01726fab0b7b9e4b6bd3691fb209fa621791f70aa630d55166c0","impliedFormat":99},{"version":"86ae83eb226a1b8e2f70de4a5610ac500ce8971b604632ce7bbcaaf3d13d399d","impliedFormat":99},{"version":"7f6ab1efc3c2fc908ed9c93f84f68a582d9167f60937235f2cd301478d7f5e94","impliedFormat":99},{"version":"b2751a77782a0dd713289c2e086699290f993a2a6ecf39958e4b03f680b31e21","impliedFormat":99},{"version":"b3c1319feedffe7321c34b7167042ae4667b1f2cbe75c12b2665ee81ee7269ef","impliedFormat":99},{"version":"b3d81d996ec2281f859a45eb5c16df03ca64a451ea9e46dbd1f7f5bb8f0d8aa3","impliedFormat":99},{"version":"09ab28987b85ab03c2fdc78a76e48604af3c048577f4bad6948b76f8701b5827","impliedFormat":99},{"version":"966042450d726d12936da89c37879161803fe69872edf798648c31c771ca7656","impliedFormat":99},{"version":"b1ed373950c740359a135801f40b932d7f28b3fb0dba2533048d6929bdb4bf64","impliedFormat":99},{"version":"908cc3d2610b39fca4286537b475d45d956a5ac81ecfac3b36509764b4c4e68e","impliedFormat":99},{"version":"573707d1170f4357c0e62c92913db845f7aa06612da1ef594790f24407efdb75","impliedFormat":99},{"version":"fc9684e27ee9e97daee7c4baf3426d733b6e9b269dc95090c07f60bc7c7241d8","impliedFormat":99},{"version":"bdb8af267942f3ba49df1ae5bf374e7fef80d6228301a5a14ea86b8f46419a97","impliedFormat":99},{"version":"e4c4a481968f01ca4caccc4775100deb9d7c13ea9c8cad905320d8a6eb882cc8","impliedFormat":99},{"version":"1a06351c322f26ea390b5f64be6bcb4581487c56295ce7910d26fbc145c0de09","impliedFormat":99},{"version":"8ea2f6db6a13c15cd18354dc171172bd25f69fafa38e51115e7e0d73fe0e7194","impliedFormat":99},{"version":"f09fb4fd37cad325148837d5af245b810361dea1dfe3b9295ea26811641ef9c5","impliedFormat":99},{"version":"ab1ca5724fd1834ea88fc64059e496b9b81d79587879b2542dc95629cb476df6","impliedFormat":99},{"version":"1a1cf67d17bdf51b03e5738d6691791d207023bb983880cfa40e694f4c7c3658","impliedFormat":99},{"version":"dbbece8a54c84cc9a24ef369d0f61b420438f0e4a49e1cb52f6a08466fd2f2cc","impliedFormat":99},{"version":"ec4546150cc5e8289a46866e5f09468ab9f8bd0e62388307dd8278b72753f124","impliedFormat":99},{"version":"3753072c118bb577a833b2beaa6083855150899d6e9e8808f82f8c7a0c897de7","impliedFormat":99},{"version":"ecbca823128bdadfe66e895f024554ed9bb30c0557ea7d0d76840d59fa9afb58","impliedFormat":99},{"version":"dbbece8a54c84cc9a24ef369d0f61b420438f0e4a49e1cb52f6a08466fd2f2cc","impliedFormat":99},{"version":"36554ee1a2f2320d303fab7ca4b29596bb177ffe548ca8b55e5bf84f93d3470f","impliedFormat":99},{"version":"1fb315481ddd56f10db8aa5e13d692d0de320d90dd1ec681838e240f514fc8a4","impliedFormat":99},{"version":"51bbbda01d321417d621a1d5be7d4e84a27c5cc5714045e6497af440bcf936ec","impliedFormat":99},{"version":"859af863983c2fb4b676c31cfd6644058b3a2ec60dcca7afec8545a1c18bf044","impliedFormat":99},{"version":"e4abb4ac717aa2536da992f1474028555d4cb6fa58f18ccbd7c85df091343511","impliedFormat":99},{"version":"51bbbda01d321417d621a1d5be7d4e84a27c5cc5714045e6497af440bcf936ec","impliedFormat":99},{"version":"0e028e080ee6c91ac3e9bc2e7811328fecf16478aee2744196e4dba5c63a5812","impliedFormat":99},{"version":"26cd4771b2758fab625c1e4daa0255373c4217c7c2f52b9f8cee614650e395b5","impliedFormat":99},{"version":"ec9265f91fc03169f575e783fdc7eb9b91631fc9508ebc4d72b5c7376dc2838f","impliedFormat":99},{"version":"f8212c6b11820840c05fe818464cecb86d913a56efbc96366cf15eaa1fa61ea7","impliedFormat":99},{"version":"dbbece8a54c84cc9a24ef369d0f61b420438f0e4a49e1cb52f6a08466fd2f2cc","impliedFormat":99},{"version":"df705626180117f2093223856259585b3980d4c76677f86ef056be64eaf8ef10","impliedFormat":99},{"version":"5ae0421ff74c7f92f2ae6ef99a4e5a4d08cd2ab7b9035ca8dc9094f87ec00854","impliedFormat":99},{"version":"2b26e0da909c8887901e6918a600b041b8a4187e3fc1d6a97d4327cbf9d78e20","impliedFormat":99},{"version":"dbbece8a54c84cc9a24ef369d0f61b420438f0e4a49e1cb52f6a08466fd2f2cc","impliedFormat":99},{"version":"1834b47b5e9e94d65d3dbc92eb741e700fc0295afa6bf6bc9e295d85b259510e","impliedFormat":99},{"version":"a986bf4e386590ddf9e1f2ddebec670d6cfffc7cb742e88d9cbf25d8cf6c6e3c","impliedFormat":99},{"version":"51bbbda01d321417d621a1d5be7d4e84a27c5cc5714045e6497af440bcf936ec","impliedFormat":99},{"version":"0950450a9b9e60d9b2d1c6655caf0c2ec1281ddb3bed8e43c97484cfd2529cdd","impliedFormat":99},{"version":"7bb22b0e03ee4cb0ec7018ece9b4a7d1b99947ec5ce303c41eebc18427d47014","impliedFormat":99},{"version":"c02b653b1114928953e8e073fec7ba7dc07b3c09fe0fbe177b75ccf715826b2a","impliedFormat":99},{"version":"dbbece8a54c84cc9a24ef369d0f61b420438f0e4a49e1cb52f6a08466fd2f2cc","impliedFormat":99},{"version":"92e884e9398fd2f92eeb89b453527e103bd4f3522c3fcfe9b37aa2f06cb55e59","impliedFormat":99},{"version":"00a1ef46b9c5cf7cef36f691a235c0cb6e04e7953548507d66f8b7d6c76b78ab","impliedFormat":99},{"version":"d412bee4d7cce20207f126da114e0fbcdf14ae37fc72b038b993aa7d2ab90501","impliedFormat":99},{"version":"dfd85bb9018f85a16f56b2bdb06712550c72ad43771c984f0740933562716b9f","impliedFormat":1},{"version":"3e15b8234de578c958c85af5df2d74728382abdc9b8e3a09f65a2f87908dda4e","impliedFormat":1},{"version":"9f237d2411854489c7f25dbef70faf7e1b4b8c97980b1e1519de9c298093a76b","impliedFormat":1},{"version":"8899191137ddd594c771a77dbb8769b2fae098882fcb1b63e19b32cbbbb147a4","impliedFormat":1},{"version":"ab6eb60d22a5cae7ea878c3b31706dbf7f25541abc65277b42d599b3d51e6223","impliedFormat":1},{"version":"3d69c83ee1b82b7bd7124b932bdf81870d33b8e4b902569943a977853ffe7d9c","impliedFormat":1},{"version":"0ddfb5bc655babbb14fd4209c7046df88373117d4b5bdbbc1ce7bcf36dcf3bb1","impliedFormat":1},{"version":"f3d2623af0d17114598a59ea2d028636fa457b8b403a182b9836c55c92316f08","impliedFormat":1},{"version":"7df556f9fe0b9c12d556e938d6f1919dc19309fe14b148b37e499b78f61c77c4","impliedFormat":1},{"version":"781a2bd6a5143d55c8f12566093eb77ec7cde12aa1f5e463ea25fdd56e27044b","impliedFormat":1},{"version":"86414c1a9abe962df3589f780ec79573ac7e387f93945d954f98ef4082c36872","impliedFormat":1},{"version":"5f8a909527729ed82721c24c937d53c0d7f043f9ee5c4f9b3d79b083bc5c29cd","impliedFormat":1},{"version":"96ab1f14df41476d899aa51f5ebb6c6cc73da6d4c453b8f5e028e6b97d5d4978","impliedFormat":1},{"version":"26ab229375d893b57a6beb1c7bf34bdb10f44ba014f540552ec85fd12934bcf7","impliedFormat":1},{"version":"38d27d13b6e4f4a71a3e56d7d328483d545292a113226c2868c3f805bbfc1634","impliedFormat":1},{"version":"8673dbde34d0cc05894df27e4bdf5990df492c4d34c3dcd18b6e21796fd15fe4","impliedFormat":1},{"version":"d670fb759fe85972ee913251ece82ef8316ad60db7da6ebd00870bb92c508fb1","impliedFormat":1},{"version":"c9bbd1795b24b0f7812e67af7d19c67ae939e8cd1d0c3f4a1317a3f6170c99f0","impliedFormat":1},{"version":"621e56640e255a3115583c4d6312570b42817fd94dd6c06648870c1346e2b1df","impliedFormat":1},{"version":"2371fcefbf57c50dcd79fea7bbd3ccc54ec0d2abe1de504874d4efa541883f76","impliedFormat":1},{"version":"33ab90858fc316775a8ecf48a01bb79f03817c3d96bf513c09ce1b8920e4dd76","impliedFormat":1},{"version":"a231a096989e067acee6dd64155bb1e53934a61df42d6aeb99a17807c9926ad0","impliedFormat":1},{"version":"0b1d800eaf4e727eb9c714fae8fd95e55fbeaf25857b49bea790af1574be4a85","impliedFormat":1},{"version":"e59528ccbc75b696898856a059587a882067d990d76745d64dbab1e6b4398009","impliedFormat":1},{"version":"252741724c8a904d920131b9a62208da4e9799cbb144283f3545a16accb1674f","impliedFormat":1},{"version":"b39d89bd47d82b44feb9d7d435e3a55ec57c1f6cc8d2bb1ca7864b2b99bc5bc9","impliedFormat":1},{"version":"38bfe4a8dfda39d360af47539b98baa383e3c79e350e3c55a978ce956d8ed4b1","impliedFormat":1},{"version":"0fdda965c835ca890a998a10ddf96c494e82b0c12eab67ca2230f68ea093c908","impliedFormat":1},{"version":"cf4df17dcb70eee873ad6d67cfcdbffe09772b627eb98b9432377fc75ef7092b","impliedFormat":1},{"version":"965417cee04e9d38845d413e467290774dcfd7ea81423e6fca44ca3e20997873","impliedFormat":1},{"version":"4c3695b79025f04f54e74536893918730bc10eb01e00b9c2f0baad00aa2dd89c","impliedFormat":1},{"version":"873c7dd4941cc43a45ee6f02012fb8344caaf996173df73a4e19913b1141900f","impliedFormat":1},{"version":"2531cd5e023c45796ff9996f859a5cbdda14259c5dbfbe7ed52c2cd6307e6667","impliedFormat":1},{"version":"5e5e719a24e3c5b53a7540394e92e97dab206620c00229202dc9f9fd106bce33","impliedFormat":1},{"version":"523a395a32c17b2fd93560fe91ca5b0ef40b475319a1f5e1a3a1a0a16632abd7","impliedFormat":1},{"version":"0f2022020e9b48d7d3cd73d78de09d33c7a02cef7fa9176a89df855347e98ec1","impliedFormat":1},{"version":"25f0fca6b5e0b89074334e87d8061b3c08293bf0b6dcd478fff7c695b99ca3cc","impliedFormat":1},{"version":"3527f58048baa0dbbb289945ea633f8cd10b5668d9c2d29641cab2b36791df37","impliedFormat":1},{"version":"de453fbd0c1cad55c59bb10e8b3f46e7fec4bf27a8149bd052a67bdf326b97c8","impliedFormat":1},{"version":"23aabcfed54fee592a22c01224811f6ab07b3f6e5242179e8f4de147b4b66992","impliedFormat":1},{"version":"8153edd1581568d8c35ea458627a87db312dfaa86e86957e0c6506f80c90d031","impliedFormat":1},{"version":"d2d33a12a2d340da5aae54a09c1af641b4c1c5d11c10af08e40a752697ff93c9","impliedFormat":1},{"version":"621c6f56254c53e61b975c1d7794cdce092b8c058bda96ab44e7d34039915724","impliedFormat":1},{"version":"24b2524c5d0caf2f9783afb8b5c7910bb688accdc50f0f1fd09d576769ad7056","impliedFormat":1},{"version":"4ea8fbe2c5dbd1e93e5be1805e950a8aefaf9ed8bf742624ef9bbead0548cc33","impliedFormat":1},{"version":"ceef64943b889ede63bea25d79c237bbf131d149bbbdb63d960afbe4e88394cb","impliedFormat":1},{"version":"f8dbaa1df3053e1e866be6335bd0bbfb59b087100c88280e173a7f8c71489f39","impliedFormat":1},{"version":"d4e51e1e64ba79db6a4287f4be2745f699d47c4cf4b561d44d0a5739604bbf2b","impliedFormat":1},{"version":"380c4123ec4b23e7ba369c0cdb3a53cc3ad5362d0798dac28eea37dccfa2831c","impliedFormat":1},{"version":"29f80580c3ef7d5985c77e92a43e9dcda01404a3e932b3c13ad0c81bed0604b0","impliedFormat":1},{"version":"9eea219d6aee0b2828d05390fca2d97897f28dfcfc9ea28e89e49b17e181fd86","impliedFormat":1},{"version":"e822c6d5791504716b5c354f34c6359f4eb5dc58b02eeb7ca2bacaf4e12964e1","impliedFormat":1},{"version":"68c0b30766379c66ec6c29f2f28a4ef812765f16bfd8179cd3e55044e7fbc1ed","impliedFormat":1},{"version":"f6cbc3623702fbac026bd009ae0487d71929794c1abce8078caf6cacc7a29284","impliedFormat":1},{"version":"df97caa5b48b8b45ea863dd2b4520c0314c22022f20520a8733d906b5af696dd","impliedFormat":1},{"version":"f5dd94c10a58d2f2bef1d5f0bb2dc74e2d4437b0f1715b8a350756fd9f18ebcd","impliedFormat":1},{"version":"ab42549112b159f45e982a5e510d09d47ad6d698e1e3883126cb2c32502c473b","impliedFormat":1},{"version":"00165eaa48e183fb416419d196455e68a87fe91062b8c00131fce8c89f44f8ef","impliedFormat":1},{"version":"5810d9853732cacc825eed5b17a78e6690e62d25f0bbd8007836e67d1fa86e6c","impliedFormat":1},{"version":"f88965900f3eac9928f67bf05e5eff94aa36fed8a755a4ae2c24742053172094","impliedFormat":1},{"version":"eb80a37e22e70eef871f80cd04b190d8bba835f1a6096040d947fe55d62a7db9","impliedFormat":1},{"version":"d969b99d2d72b2d4739109e9c9b9f7c49d070d672f65b47f043903d4bf708845","impliedFormat":1},{"version":"dad944020c14c58d2dde4da2affd973a84f9e3e4d5101144ca6b1d658edf6283","impliedFormat":1},{"version":"e0eecfdf06504177423ea604c5b7036aea5c05cff8e6b5c5bf65e09546668e39","impliedFormat":1},{"version":"9ed6d88f27fe2e8fcfd9a4cbfe5c950b212baeba9accb6d83666600addd4dfcc","impliedFormat":1},{"version":"6f4ee252d6e45ef593d7b0f6cbc424db48c2aa0b864b0fa70a6c075bcab0a02e","impliedFormat":1},{"version":"9152e9cb89776f3cca3aea54a1ffd516d0d7e985d2dfdd68dcdb4f7e5b9a9b71","impliedFormat":99},{"version":"ef0404706ce91746197fd5d39f7e3c4e92ad7bb83434d95d2cb29a4d549c7cfc","impliedFormat":99},{"version":"87a82461d9c74c259975943afba224f174b6929b8b69f19316f594029b110dd9","impliedFormat":99},{"version":"51b42b886fcf0a19b9ff49868f4e6a6c43bb63c3adbeca84ea5a4f2ed4d5138a","impliedFormat":99},{"version":"1825792255e342f5af15246fc9619e3759af6be25a4cc968999bea915e463a45","impliedFormat":99},{"version":"063c5946311513d1719f294be42441c737e304df8c0cf7faa2a8a68bc59cbdf5","impliedFormat":99},{"version":"cbd4af10b152134ef5c1ec62fc2e45b008eab6c6fea2f63abe8e60a5965ea73e","impliedFormat":99},{"version":"bee7e164f33a358aaf7d2c8d33fb2c57ad5ea0b4472f23aa29de0758a721b54f","impliedFormat":99},{"version":"ca5f5d0a3cae743b58dcbad8f67d45b46387e4677a971587dfab16a748d55a31","impliedFormat":99},{"version":"c2cf729834036adabccce9347584c8ad37e5950825b72c671df91578e7282ed1","impliedFormat":99},{"version":"48fe338e450cecf8f60b99b32ec056464690e0881a708623067295217461e584","impliedFormat":99},{"version":"9d31a8af6bc7c4b99b655f057cc1f07343fe9616bf794175ae4b6173743cc508","impliedFormat":99},{"version":"ea7feac90d8512b4f2ff6e1b4efb4367aa4a22912c356d6d98bc7f8a858b6dbb","impliedFormat":99},{"version":"063c5946311513d1719f294be42441c737e304df8c0cf7faa2a8a68bc59cbdf5","impliedFormat":99},{"version":"ac424eae6846b72fe5f0d5f699d198cb8aeae735924d0ea2ceb7a314e9eee4a1","impliedFormat":99},{"version":"9d4b95ab06738e0127a372f236d9b2428598709094548d63f51a8478b0253e2b","impliedFormat":99},{"version":"afe59e36dbf265d69eb9ef811fe4f15f4999570185587a7fb3aa741254c3ecd5","impliedFormat":99},{"version":"323f44e24829feb36b18b0d77c13d2dbcb5813fcc80ba2c5a6235f17741cff7f","impliedFormat":99},{"version":"178728484e7a6c10ceaaaa8e4ccca3c3dbe1c4c0dc74a9b101036f9ab0bdbcb0","impliedFormat":99},{"version":"dbbece8a54c84cc9a24ef369d0f61b420438f0e4a49e1cb52f6a08466fd2f2cc","impliedFormat":99},{"version":"1d0567a40b032ad0d8ef91ded14739dd78230d9a79b209a038d9aa74b49cfaf9","impliedFormat":99},{"version":"68465a68993beea899574bd899cb5d8fc7e1189d716bff6426836c49ff2c76d9","impliedFormat":99},{"version":"59263852a2516cf57e49d545fd52e923359624cba5f8cf595f48ae404fdf7524","impliedFormat":99},{"version":"1151f7b54019b6eff96526ec72d542274bd73632d473486d6fecd8084e53201e","impliedFormat":99},{"version":"edf122f1acbc0843190dc272de1ac542c77943477a8ab6110574cb632b7e1b01","impliedFormat":99},{"version":"ef01b6992842eeb7c25e1807e6d3936f134f498e5f030877e37340221800c501","impliedFormat":99},{"version":"726dd160da57c905467702b85d53263d92b3a87d5a05b289f0acd2b98c000c55","impliedFormat":99},{"version":"21312a463d81457867895b35095cfd827ec775142e5e113071c4714d24d45b39","impliedFormat":99},{"version":"6fc5b8953b8fb338a0839bb9e2edfdf6b80e6a14b169b54c5ec94899f4b6898b","impliedFormat":99},{"version":"7937d37b8615603f403e19a9b0e4357a214fc646fe4f50a7ea96f35aa90fb74a","impliedFormat":99},{"version":"dbbece8a54c84cc9a24ef369d0f61b420438f0e4a49e1cb52f6a08466fd2f2cc","impliedFormat":99},{"version":"c762aeec152965fb0de7e6c5cc500df1ab788acd86891488a6b1e39b782b1cc9","impliedFormat":99},{"version":"988e41d18cfb7249f367afaa04c1ef20dcee125b24b0ab22fa729ffadb302855","impliedFormat":99},{"version":"9d2679c417dffb67d7f5e8b1a29909fdeef58dfccff9a2bd3ee92bf7c9417b04","impliedFormat":99},{"version":"1ace3b57c44f6b67bbecc38bcd1001f4cdbe8cae9f2b1b682c5b723c5a396265","impliedFormat":99},{"version":"644979a865433b3107134325fabe637c2664878a5045170455a12e958426ed53","impliedFormat":99},{"version":"4f54c7fb5961a99ec9e3903f7c36705c8924bf8aa1b2101a6a53ef633b38ca4d","impliedFormat":99},{"version":"eb3b31f11e913d8efbe4b1c9a731c0750eba9e8ac7c64f961fe2b8747a0d8b55","impliedFormat":99},{"version":"d60d7172b4d4708afc0ec411eeb6ae889e8d87c25baae64cd70a8f77f003d751","impliedFormat":99},{"version":"106c47488674753b587c532bba6980d1010a1440c47ce25bd4ee9d65c4fcf9cf","impliedFormat":99},{"version":"f25408ad90b9a433f4124f2408ec5d71985514dcb25da5146077824d30a9262d","impliedFormat":99},{"version":"61c0ca22963ab7a232e6ff1f0e7558a3d90cbc489288bf5c2bceeb374196bc2a","impliedFormat":99},{"version":"3f19ba14366c8314024981eff375a3c4c82b5e8a1571f99021a2c595a58b7787","impliedFormat":99},{"version":"381feb39bec29f74e9a8762914cc44302fd5bb28f4a4167d19a08d0a2e6fbbc9","impliedFormat":99},{"version":"1e13042b06c91a57e17cadbf39df975995858b313efb976a17f03b1ed4ac941e","impliedFormat":99},{"version":"1bf7f34549f798ce83855e7852f40fe1b161a0a99cc2ec710fd3033b6c708efa","impliedFormat":99},{"version":"ffa11a6abf9bec5461a52f9540573d9587f5d5cfdf44e02ad69349004aeb0bf7","impliedFormat":99},{"version":"228780797f100a9097434e10e0a8881e9da01c40cd10d924c36ec3d29be99cd4","impliedFormat":99},{"version":"ad6597894af9ed66e55c285cb151132cf881c6a5a66550ac014c30d64654fb52","impliedFormat":99},{"version":"4c46080a788a4f839fd6e79089994bbc88b96a4e4223a9bf8f29bfa709e12609","impliedFormat":99},{"version":"8a33af7d173a56d3cfa0fd4468262da3d3ddc7cf56116ae0025b69fa0b4e5d5e","impliedFormat":99},{"version":"ba9b62f2363f96b897ff129d9186cf55ea6a03a8a680cb4797343c34232efce7","impliedFormat":99},{"version":"efcfadc996b81902d8255b8fee04921ad6ef19bc5d870c24661bfc977fbced8a","impliedFormat":99},{"version":"e1f4d2566bc0f54666b9d2f2898551872a3ad4e50de55d7caaa22614df8dc969","impliedFormat":99},{"version":"309ad3b88b4421dbe229aeea35ff3dffce9422e98f3e36da382571ccb497d0e6","impliedFormat":99},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":99},{"version":"79b03a391d27ebbcc950ee8e7d78fe638c7d4ec7a4080e6dacc9f91f841fec8a","impliedFormat":99},{"version":"23f0e9d8fabba6af02f11c4941f9a2ce03eca49a9de8366472ccf0bdb24022c5","impliedFormat":99},{"version":"afb0b71ed7867fc32b57f7d71b7a39bacc3c2d88cac6901a2eaab0e8197c3f2a","impliedFormat":99},{"version":"e513da1a3ebfdd6862962fb5fca869ad3f71de6ba8af5b55496953f1e363f224","impliedFormat":99},{"version":"f54fcff687473488b9e395e102d51ab88d474bada45f0356cbab6ad41779c33b","impliedFormat":99},{"version":"33c72ffe8e3337d0094324ff7d927ff660e4a12c5f0ee4aef3f62665d27574d5","impliedFormat":99},{"version":"c618ab843c5d3e5b80f7fff99894f434689a83aef4bbda2e2ff53530060dc20b","impliedFormat":99},{"version":"722f091a793140b7a17842b4514c3562c7ebaceb7408a3bf2e5e48faa719ce45","impliedFormat":99},{"version":"c7a5628418ad6e46974eb6247b5c66012ce5c8294d7461c01b5e9249cf8d9dc7","impliedFormat":99},{"version":"7a985ca144078c55ff33db714ae38156421c632efc598d65edec6c8ead046eb5","impliedFormat":99},{"version":"5a969f536f50d52ffa08c04e5b5d02da7a3233d10ebfd48e98d8eefc3a24bee4","signature":"1e9d9700fa21e1e3480d956cb174cfe148538600756f98d9c0af5c7f2b41e989"},{"version":"da2bfdfb44dd4b64a8232c394f976166251ae15e8be86a0c1702e4af6aac4b64","signature":"1a6c23ea651200f52e4cf2306d7c2af89cc4bba38168be39c63b6e64a9f6d8d2"},{"version":"2e90de77f0faefd0a28cf2e9fdf51c5db02abb60e838215ea2bc35acc4185b8c","signature":"e6651ac2fb47f61c8e52f91bdd294aeefcd02da3013ed4ef87068a6930f88486"},{"version":"b80c780c52524beb13488942543972c8b0e54400e8b59cee0169f38d0fabb968","impliedFormat":1},{"version":"843772e418a5fbe2d062316490eeb0349ce9513a0773ce751fedc9d435f2fa0f","impliedFormat":1},{"version":"14dea907a1d49d236e77b00a0c1968b8ca1f6c28fd9fbc90e515573090ce255c","impliedFormat":1},{"version":"42e8d611a11ffaeb683e12f1c913b6cb35b6745f15976efbef313cdd187143bb","impliedFormat":1},{"version":"82a08eb132e8a6e5d9a40f211549af26aa7d08cbdcc3a8de8c785f8b3a61e1b9","impliedFormat":1},{"version":"e5e7bac8ba2daab62e2d07c8c350d63f01dfe32af4b0b1ce683bf792f3ecd0f7","impliedFormat":1},{"version":"4d0f5e6f876b9942c224d9677b03cdbce1504551671db4e4e60a4834ffa9dda0","impliedFormat":1},{"version":"3bec87611d4741499b645af03bba9a41e7519ae855ebcafb3db244c21b907e4f","impliedFormat":1},{"version":"e63efe908892e6a7f09c8b97ead3440d7da737b848daa0a577ae667173445ead","impliedFormat":1},{"version":"4c8533a16f6c4597d9fc7c0c7f21a3908084a819a111d5637ff2defa7b345bfe","impliedFormat":1},{"version":"57b41a36f36758b39c7d0402350ca4971dedeb3c2a63cecdac060a5320c04c55","impliedFormat":1},{"version":"29972445d3c4c90f33eff093c52daabff847185f23e006b906ae582db47c1b76","impliedFormat":1},{"version":"c64d10c169ca1c7e662f229f619455d3710a9be5caea81ac890939dcef6eb639","impliedFormat":1},{"version":"311d23b5021473e0dec2266bd53aed658b96e1610f2bf5b00d4ede63f52458e4","impliedFormat":1},{"version":"403303182c36648089590004be2e9d41327dbbf49d2bcb49300e079abf30a48a","impliedFormat":1},{"version":"47f3f9181c8899d12ec3085fd59d17f8fd34d827771a3d3ffb08e217e3f4045e","impliedFormat":1},{"version":"064252d997cd14b1baa1ba7df575a1fa127aec5c50c6795ffb420377931f8927","impliedFormat":1},{"version":"314f629df4188e92ce501c35ad66513232d8ea54a94c9c55052c6cc6a57cb404","impliedFormat":1},{"version":"619a9313defd6fcf0951b7700bafd0270db7c54266ac279848bb71a182a91b39","impliedFormat":1},{"version":"9c3c3bda90321687067b0fa23d0348ca5091faef71281d375a8c6b1dd646602d","impliedFormat":1},{"version":"a1289a4bc5fc40c5ee4c0bb1123b686aeba2c617021a47dc6f0f936b18287428","impliedFormat":1},{"version":"175c039f6d6d6b441d6929e35a52d983fc31aae1d90cc082499472be847bff44","impliedFormat":1},{"version":"4906f21f2637d60c009ba11fadcb55f3461d32fd4053bdc470837e2d492a4949","impliedFormat":1},{"version":"f33b9fd731f271ed3e635214adb7f13c8061a733d9e0e4f3f7253e54c633e1d5","impliedFormat":1},{"version":"0c609838f7b5a10b03fef513bbfd7d056bf0e6fb0cfae6baa0be1d85341dc295","impliedFormat":1},{"version":"f7988a5d76deb4c742941e891dd2a6f7640b8c67ef4c10dd780be80d04282510","impliedFormat":1},{"version":"92567a54f7e9d10ae38666c6182d757309e94e36b763044b2aefbe6b31e443ea","impliedFormat":1},{"version":"f41fa86abb13df8c06e3dd545175772fd6ac7f40639f2437fbcaeeef9251536d","impliedFormat":1},{"version":"eb1f0079ad411d3d282a29c721c89ab4878d514eb361ac7c05041147c8b99a89","impliedFormat":1},{"version":"790551602f511013dbd06590159e6aebd44a15127a7ddec7eaeeddd6af7eb239","impliedFormat":1},{"version":"36b408cad01f9cb2739b34a99b333f2ee8e9b27c4363176ea9b4aafd35b692b0","impliedFormat":1},{"version":"841aa7ecb51c9d4046f6c33c640b0366849e21fa0de6498a08158a1c5dd1506e","impliedFormat":1},{"version":"b8513a4e4d3f02612d111adbbde8ae00a6adfdb33892fb72e84662144899b562","impliedFormat":1},{"version":"b87d1a554ec977c7a7797601313412b6769544f638104787b0136d4fd6fb12f3","impliedFormat":1},{"version":"841aa7ecb51c9d4046f6c33c640b0366849e21fa0de6498a08158a1c5dd1506e","impliedFormat":1},{"version":"147abcf1d48a19553366fec6b72191ecf6f83d8e4c1260436c4f854883bba65c","impliedFormat":1},{"version":"61e8f63ea286d27ce20bc07baed9a30b0e18b0a3ae66b6a9e6aa7353e37c5111","impliedFormat":1},{"version":"5c8a7c35b3ededbbf0b7ddbd5e99a50061d424946da2ed5abbbbbdb3989effd0","impliedFormat":1},{"version":"ee020464da44a0b6a825c9a0d3a2ecab3e4c028eb005b4fce44c53b4959114fd","impliedFormat":1},{"version":"7e88d2eef3de0e141de5c7b3140c5aade44be358a5a6ed30a7595b625868cf77","impliedFormat":1},{"version":"f738149a907a25b221347bac1f18d712e84fb5f4ef9cabdd80f2f8e7a913e35c","impliedFormat":1},{"version":"9e3ac0be1bae2b985b57fd0d4c87b8eb4b39fe0d498fe5b1d6d72402e5a0c523","impliedFormat":1},{"version":"fdbcc3f4c8b0aec2a53cd2949ed1366aa890c48285d216a7b2e4df76ca1702a7","impliedFormat":1},{"version":"c746651f3145fc12d1efa456fe7eb530102acdb242a2e5720ab03bf790fab81e","impliedFormat":1},{"version":"bfea1dc7fa119165ec11be5efceb5305638bfe20cb9ab26f770b9bbaac393106","impliedFormat":1},{"version":"1c5f02fe59be094446d0c94115aa980d43c50dcc65ff95410fbae46982b0c4a7","signature":"2bca72be9ea45128710d0962aca2ad39d071d0b0ecf688e7495fabaf097eac24"},{"version":"2f921d06bea8b28ab65dddfa4407537d08be2c47b319e12791c42a52ade12318","signature":"75fcc9b1b71cd74713931f7357580f05d4481d045aef310b8d7acb7c24afba1e"},{"version":"a159d8ac6a2469eed5b31cdb411d73cedd0b9af7badd2b2cc887ab989b5a3c9b","signature":"1e697c518086061a1a3a37b5af114a3ea94c1699862cee5a02623e9391f1584d"},{"version":"f75f718c165fd074a3caeb73d7e63cfb76a4809a261c06b252705c6115bcbe9b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d153a11543fd884b596587ccd97aebbeed950b26933ee000f94009f1ab142848","affectsGlobalScope":true,"impliedFormat":1},{"version":"0ccdaa19852d25ecd84eec365c3bfa16e7859cadecf6e9ca6d0dbbbee439743f","affectsGlobalScope":true,"impliedFormat":1},{"version":"cc2110f7decca6bfb9392e30421cfa1436479e4a6756e8fec6cbc22625d4f881","affectsGlobalScope":true,"impliedFormat":1},{"version":"096116f8fedc1765d5bd6ef360c257b4a9048e5415054b3bf3c41b07f8951b0b","affectsGlobalScope":true,"impliedFormat":1},{"version":"e5e01375c9e124a83b52ee4b3244ed1a4d214a6cfb54ac73e164a823a4a7860a","affectsGlobalScope":true,"impliedFormat":1},{"version":"f90ae2bbce1505e67f2f6502392e318f5714bae82d2d969185c4a6cecc8af2fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"4b58e207b93a8f1c88bbf2a95ddc686ac83962b13830fe8ad3f404ffc7051fb4","affectsGlobalScope":true,"impliedFormat":1},{"version":"1fefabcb2b06736a66d2904074d56268753654805e829989a46a0161cd8412c5","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"c18a99f01eb788d849ad032b31cafd49de0b19e083fe775370834c5675d7df8e","affectsGlobalScope":true,"impliedFormat":1},{"version":"5247874c2a23b9a62d178ae84f2db6a1d54e6c9a2e7e057e178cc5eea13757fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"cdcf9ea426ad970f96ac930cd176d5c69c6c24eebd9fc580e1572d6c6a88f62c","impliedFormat":1},{"version":"23cd712e2ce083d68afe69224587438e5914b457b8acf87073c22494d706a3d0","impliedFormat":1},{"version":"156a859e21ef3244d13afeeba4e49760a6afa035c149dda52f0c45ea8903b338","impliedFormat":1},{"version":"10ec5e82144dfac6f04fa5d1d6c11763b3e4dbbac6d99101427219ab3e2ae887","impliedFormat":1},{"version":"615754924717c0b1e293e083b83503c0a872717ad5aa60ed7f1a699eb1b4ea5c","impliedFormat":1},{"version":"14e9acf826baba0ef4b5665704084896e7bcc06f65a9ab13af7e93d27d6b7069","impliedFormat":1},{"version":"68834d631c8838c715f225509cfc3927913b9cc7a4870460b5b60c8dbdb99baf","impliedFormat":1},{"version":"21adf13435b9b748529c8cedf80f884e5130b9684188120a686cd2b26a2059c7","impliedFormat":1},{"version":"eec76bf6b9346f3f95fa402621b889489e96930e72295b0369022f332e9b4a6a","impliedFormat":1},{"version":"0ecd58f413f9bc3b7d4383eae31b0c8fc576985cd7404d6f99f8c643543ade74","impliedFormat":1},{"version":"ea6bc8de8b59f90a7a3960005fd01988f98fd0784e14bc6922dde2e93305ec7d","impliedFormat":1},{"version":"36107995674b29284a115e21a0618c4c2751b32a8766dd4cb3ba740308b16d59","impliedFormat":1},{"version":"914a0ae30d96d71915fc519ccb4efbf2b62c0ddfb3a3fc6129151076bc01dc60","impliedFormat":1},{"version":"9c32412007b5662fd34a8eb04292fb5314ec370d7016d1c2fb8aa193c807fe22","impliedFormat":1},{"version":"7fd1b31fd35876b0aa650811c25ec2c97a3c6387e5473eb18004bed86cdd76b6","impliedFormat":1},{"version":"4d327f7d72ad0918275cea3eee49a6a8dc8114ae1d5b7f3f5d0774de75f7439a","impliedFormat":1},{"version":"6ebe8ebb8659aaa9d1acbf3710d7dae3e923e97610238b9511c25dc39023a166","impliedFormat":1},{"version":"e85d7f8068f6a26710bff0cc8c0fc5e47f71089c3780fbede05857331d2ddec9","impliedFormat":1},{"version":"7befaf0e76b5671be1d47b77fcc65f2b0aad91cc26529df1904f4a7c46d216e9","impliedFormat":1},{"version":"0a60a292b89ca7218b8616f78e5bbd1c96b87e048849469cccb4355e98af959a","impliedFormat":1},{"version":"0b6e25234b4eec6ed96ab138d96eb70b135690d7dd01f3dd8a8ab291c35a683a","impliedFormat":1},{"version":"9666f2f84b985b62400d2e5ab0adae9ff44de9b2a34803c2c5bd3c8325b17dc0","impliedFormat":1},{"version":"40cd35c95e9cf22cfa5bd84e96408b6fcbca55295f4ff822390abb11afbc3dca","impliedFormat":1},{"version":"b1616b8959bf557feb16369c6124a97a0e74ed6f49d1df73bb4b9ddf68acf3f3","impliedFormat":1},{"version":"5b03a034c72146b61573aab280f295b015b9168470f2df05f6080a2122f9b4df","impliedFormat":1},{"version":"40b463c6766ca1b689bfcc46d26b5e295954f32ad43e37ee6953c0a677e4ae2b","impliedFormat":1},{"version":"249b9cab7f5d628b71308c7d9bb0a808b50b091e640ba3ed6e2d0516f4a8d91d","impliedFormat":1},{"version":"d33ce35e3f9cfcc1d94eca415bdd3bde94d5b153ffdd33e6c4455c029986c630","impliedFormat":1},{"version":"80aae6afc67faa5ac0b32b5b8bc8cc9f7fa299cff15cf09cc2e11fd28c6ae29e","impliedFormat":1},{"version":"f473cd2288991ff3221165dcf73cd5d24da30391f87e85b3dd4d0450c787a391","impliedFormat":1},{"version":"499e5b055a5aba1e1998f7311a6c441a369831c70905cc565ceac93c28083d53","impliedFormat":1},{"version":"8aee8b6d4f9f62cf3776cda1305fb18763e2aade7e13cea5bbe699112df85214","impliedFormat":1},{"version":"98498b101803bb3dde9f76a56e65c14b75db1cc8bec5f4db72be541570f74fc5","impliedFormat":1},{"version":"4dc59f6e1dbf3d5f66660fceabe6c174d3261b37b696ae1854f0dbaf255fc753","impliedFormat":1},{"version":"5d0375ca7310efb77e3ef18d068d53784faf62705e0ad04569597ae0e755c401","impliedFormat":1},{"version":"59af37caec41ecf7b2e76059c9672a49e682c1a2aa6f9d7dc78878f53aa284d6","impliedFormat":1},{"version":"addf417b9eb3f938fddf8d81e96393a165e4be0d4a8b6402292f9c634b1cb00d","impliedFormat":1},{"version":"436d7b4543b340b0f3eef4310d524242e41369b9652aa9c70428767c4dcac455","impliedFormat":1},{"version":"adf27937dba6af9f08a68c5b1d3fce0ca7d4b960c57e6d6c844e7d1a8e53adae","impliedFormat":1},{"version":"12950411eeab8563b349cb7959543d92d8d02c289ed893d78499a19becb5a8cc","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"114f493b30f364255290472111b5a4791d5902c308645670cd0401429cbc6930","impliedFormat":1},{"version":"c3f5289820990ab66b70c7fb5b63cb674001009ff84b13de40619619a9c8175f","affectsGlobalScope":true,"impliedFormat":1},{"version":"b3275d55fac10b799c9546804126239baf020d220136163f763b55a74e50e750","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa68a0a3b7cb32c00e39ee3cd31f8f15b80cac97dce51b6ee7fc14a1e8deb30b","affectsGlobalScope":true,"impliedFormat":1},{"version":"1cf059eaf468efcc649f8cf6075d3cb98e9a35a0fe9c44419ec3d2f5428d7123","affectsGlobalScope":true,"impliedFormat":1},{"version":"6c36e755bced82df7fb6ce8169265d0a7bb046ab4e2cb6d0da0cb72b22033e89","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"7a93de4ff8a63bafe62ba86b89af1df0ccb5e40bb85b0c67d6bbcfdcf96bf3d4","affectsGlobalScope":true,"impliedFormat":1},{"version":"90e85f9bc549dfe2b5749b45fe734144e96cd5d04b38eae244028794e142a77e","affectsGlobalScope":true,"impliedFormat":1},{"version":"e0a5deeb610b2a50a6350bd23df6490036a1773a8a71d70f2f9549ab009e67ee","affectsGlobalScope":true,"impliedFormat":1},{"version":"d2ae155afe8a01cc0ae612d99117cf8ef16692ba7c4366590156fdec1bcf2d8c","impliedFormat":1},{"version":"3f5e5d9be35913db9fea42a63f3df0b7e3c8703b97670a2125587b4dbbd56d7c","impliedFormat":1},{"version":"c8b8968311ec4e5e97b7b5fb8a65efaba455db9bdcfd7fff7fb15f6e317bfba0","impliedFormat":1},{"version":"57c23df0b5f7a8e26363a3849b0bc7763f6b241207157c8e40089d1df4116f35","affectsGlobalScope":true,"impliedFormat":1},{"version":"3b8bc0c17b54081b0878673989216229e575d67a10874e84566a21025a2461ee","impliedFormat":1},{"version":"5b0db5a58b73498792a29bfebc333438e61906fef75da898b410e24e52229e6f","impliedFormat":1},{"version":"dbe055b2b29a7bab2c1ca8f259436306adb43f469dca7e639a02cd3695d3f621","impliedFormat":1},{"version":"1678b04557dca52feab73cc67610918a7f5e25bfdba3e7fa081acd625d93106d","impliedFormat":1},{"version":"aecbf1d9e6a18dab7d92ef8a89a1444b47e1eb6134cb2bb776a26d55ff58c29a","impliedFormat":1},{"version":"2ea729503db9793f2691162fec3dd1118cab62e96d025f8eeb376d43ec293395","impliedFormat":1},{"version":"9ec87fea42b92894b0f209931a880789d43c3397d09dd99c631ae40a2f7071d1","impliedFormat":1},{"version":"c68e88cdfadfb6c8ba5fc38e58a3a166b0beae77b1f05b7d921150a32a5ffb8d","impliedFormat":1},{"version":"2bc7aa4fba46df0bd495425a7c8201437a7d465f83854fac859df2d67f664df3","impliedFormat":1},{"version":"41d17e1ad9a002feb11c8cdd2777e5bbc0cdb1e3f595d237e4dded0b6949983b","impliedFormat":1},{"version":"1fede9296beac11ce8e6b425396a1791f64341f2be85deebb6286faf6e16306e","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce697b6a251d9cad53998c7fd3098072df883b525ec45d83530e434dc6d80dc6","impliedFormat":1},{"version":"719412f054e6ecc35489462c9a21bab0323d173a7d04e55b0ace4b5d86fbeb07","impliedFormat":1},{"version":"0eb5d0cbf09de5d34542b977fd6a933bb2e0817bffe8e1a541b2f1ad1b9af1ff","impliedFormat":1},{"version":"fac3e88881b35d3a757ed891ac912b2674792c25e2a1a74e1f5fbc72d19a9792","impliedFormat":1},{"version":"2c2bdaa1d8ead9f68628d6d9d250e46ee8e81aa4898b4769a36956ae15e060fe","impliedFormat":1},{"version":"c32c840c62d8bd7aeb3147aa6754cd2d922b990a6b6634530cb2ebdce5adc8e9","impliedFormat":1},{"version":"5ff4433a2deae4f85ab1377e90a7554ce6b47ae51c69a84ca30a6e22fae85834","impliedFormat":1},{"version":"82b91e4e42e6c41bc7fc1b6c2dc5eba6a2ba98375eb1f210e6ff6bba2d54177e","impliedFormat":1},{"version":"c1fa52b3d014001e8662fa2669d90ea15373958a288e3b83a3b621733d25292a","affectsGlobalScope":true,"impliedFormat":1},{"version":"cbed824fec91efefc7bbdcb8b43d1a531fdbebd0e2ef19481501ff365a93cb70","impliedFormat":1},{"version":"d0716593b3f2b0451bcf0c24cfa86dec2235c325c89f201934248b7c742715fc","impliedFormat":1},{"version":"ec501101c2a96133a6c695f934c8f6642149cc728571b29cbb7b770984c1088e","impliedFormat":1},{"version":"b214ebcf76c51b115453f69729ee8aa7b7f8eccdae2a922b568a45c2d7ff52f7","impliedFormat":1},{"version":"429c9cdfa7d126255779efd7e6d9057ced2d69c81859bbab32073bad52e9ba76","impliedFormat":1},{"version":"2991bca2cc0f0628a278df2a2ccdb8d6cbcb700f3761abbed62bba137d5b1790","impliedFormat":1},{"version":"5e66972e83eb4dc7123939bf816e6cbd9ad81af5552db1cab84e6bd9c64d2ecc","affectsGlobalScope":true,"impliedFormat":1},{"version":"230763250f20449fa7b3c9273e1967adb0023dc890d4be1553faca658ee65971","impliedFormat":1},{"version":"c3e9078b60cb329d1221f5878e88cecfa3e74460550e605a58fcfb41a66029ff","impliedFormat":1},{"version":"8413d0641f293aed551c7464615b770d34a02dedede889b9591172287d68e773","impliedFormat":1},{"version":"0ea59f7d3e51440baa64f429253759b106cfcbaf51e474cae606e02265b37cf8","impliedFormat":1},{"version":"bc18a1991ba681f03e13285fa1d7b99b03b67ee671b7bc936254467177543890","impliedFormat":1},{"version":"1b241e24f3227d078c06aeda6e050187ad59a4e591f4467abed44d92b084e08d","impliedFormat":1},{"version":"fa94bbf532b7af8f394b95fa310980d6e20bd2d4c871c6a6cb9f70f03750a44b","impliedFormat":1},{"version":"7fde0e1be5c8be204ffbf428abfcf01da2eb0f130e1bc3f539eb7275f4fd1f58","impliedFormat":1},{"version":"e284328553df5f425a5d33d36a0c3fa66b46af9d097cad6f4d2e8696dfdeb0f1","affectsGlobalScope":true,"impliedFormat":1},{"version":"7fa2214bb0d64701bc6f9ce8cde2fd2ff8c571e0b23065fa04a8a5a6beb91511","impliedFormat":1},{"version":"f36b3fbe2be150a9ca140da48593f21e6a8172004f92ddc549b43efec39f3e54","impliedFormat":1},{"version":"f1c93e046fb3d9b7f8249629f4b63dc068dd839b824dd0aa39a5e68476dc9420","impliedFormat":1},{"version":"016b29bf4926b80255a108c53a1451717350059da04fcae64d1075f5e93bbb39","impliedFormat":1},{"version":"841983e39bd4cbb463be385e92fda11057cab368bf27100a801c492f1d86cbaa","impliedFormat":1},{"version":"1c4f139ade4f6ebf45463505f8155173e5d7a5305e50e0aae0a5e712d6ff3b48","impliedFormat":1},{"version":"e16b319e5aca1031168de823c4946ff8e29629c4c8cc0ec0fcfe2a8ab2155043","impliedFormat":1},{"version":"e4156ddb25aa0e3b5303d372f26957b36778f0f6bbd4326359269873295e3058","affectsGlobalScope":true,"impliedFormat":1},{"version":"cc1b433a84cae05ddc5672d4823170af78606ad21ecef60dbc4570190cbf1357","impliedFormat":1},{"version":"9d3821bc75c59577e52643324cec92fc2145642e8d17cf7ee07a3181f21d985d","impliedFormat":1},{"version":"7f78cfb2b343838612c192cb251746e3a7c62ac7675726a47e130d9b213f6580","impliedFormat":1},{"version":"201db9cf1687fab1adf5282fcba861f382b32303dc4f67c89d59655e78a25461","impliedFormat":1},{"version":"2c3c5c0f54055e87640f5d233716fd889f3034fc7911d603b642369b0dbeb2a7","impliedFormat":1},{"version":"0a20eaf2e4b1e3c1e1f87f7bccb0c936375b23b022baeea750519b7c9bc6ce83","impliedFormat":1},{"version":"b484ec11ba00e3a2235562a41898d55372ccabe607986c6fa4f4aba72093749f","impliedFormat":1},{"version":"a16b91b27bd6b706c687c88cbc8a7d4ee98e5ed6043026d6b84bda923c0aed67","impliedFormat":1},{"version":"1c9e5b1a17b1fc9b3711fb36e0690421261ab2880f15b145155b5b2ba2ab6c2d","impliedFormat":1},{"version":"99ab6d0d660ce4d21efb52288a39fd35bb3f556980ec5463b1ae8f304a3bbc85","impliedFormat":1},{"version":"6eeded8c7e352be6e0efb83f4935ec752513c4d22043b52522b90849a49a3a11","impliedFormat":1},{"version":"6c1ad90050ffbb151cacc68e2d06ea1a26a945659391e32651f5d42b86fd7f2c","impliedFormat":1},{"version":"afa1c49f8e559e413d57343339db857d2a8159435cf9cf7d4deb41718fff1b88","impliedFormat":1},{"version":"6953d7597831d0860c7034cf4f0419687d263b6b98a4b32e37ce6d49615c36e2","impliedFormat":1}],"root":[60,[64,66],[69,75],[276,278],[324,327]],"options":{"composite":true,"declaration":true,"declarationMap":true,"esModuleInterop":true,"module":7,"outDir":"./dist","rootDir":"./src","skipLibCheck":true,"strict":true,"target":9},"referencedMap":[[297,1],[298,1],[300,2],[307,3],[299,1],[306,4],[302,1],[303,5],[305,6],[301,1],[304,1],[286,7],[280,7],[323,8],[322,9],[321,10],[285,11],[288,12],[282,13],[281,14],[296,15],[320,16],[287,17],[319,18],[289,19],[294,20],[291,21],[292,22],[290,23],[295,24],[293,25],[284,26],[313,27],[312,27],[310,27],[314,28],[311,27],[316,27],[318,29],[309,7],[317,7],[283,30],[308,27],[315,7],[62,31],[63,32],[61,27],[90,27],[146,33],[147,34],[148,35],[149,35],[145,35],[150,34],[151,34],[157,36],[152,33],[153,34],[144,27],[154,34],[155,34],[156,33],[174,37],[175,27],[176,38],[177,37],[178,38],[179,38],[181,39],[182,27],[183,40],[184,38],[185,38],[193,41],[186,42],[187,37],[188,42],[189,43],[190,43],[191,43],[192,38],[180,27],[158,27],[159,38],[160,44],[161,44],[162,44],[163,44],[164,44],[165,44],[166,38],[173,45],[167,38],[168,44],[169,44],[170,44],[171,44],[172,38],[194,27],[195,38],[196,38],[197,38],[198,38],[200,38],[199,38],[203,46],[201,27],[202,38],[209,47],[208,48],[205,49],[204,27],[207,50],[206,50],[142,27],[213,51],[143,7],[212,52],[210,53],[211,53],[216,27],[217,54],[220,55],[218,56],[219,53],[76,7],[77,7],[78,7],[79,7],[80,7],[81,7],[82,7],[83,7],[84,7],[85,7],[86,7],[87,7],[88,7],[89,7],[94,57],[107,58],[95,7],[96,7],[97,7],[98,7],[101,59],[102,7],[103,7],[104,7],[105,7],[106,7],[108,7],[109,27],[110,27],[111,7],[114,60],[112,61],[113,62],[115,57],[118,63],[116,64],[117,65],[92,66],[121,67],[119,61],[120,68],[124,69],[122,61],[123,68],[125,62],[241,70],[126,7],[129,71],[127,61],[128,68],[130,7],[133,72],[131,61],[132,62],[136,73],[134,61],[135,68],[224,68],[225,65],[237,7],[240,74],[238,61],[239,75],[230,68],[137,7],[140,76],[138,61],[139,68],[141,7],[215,77],[214,78],[221,79],[223,80],[222,79],[226,7],[229,81],[227,61],[228,75],[231,7],[236,82],[232,61],[235,7],[233,7],[234,75],[275,83],[243,61],[244,61],[245,61],[242,7],[246,61],[247,61],[248,61],[269,61],[264,84],[249,61],[272,85],[250,61],[251,61],[252,61],[266,61],[253,61],[254,61],[267,61],[255,61],[265,27],[270,61],[271,61],[256,61],[257,61],[268,86],[258,61],[100,61],[259,61],[260,61],[261,61],[262,61],[99,27],[263,87],[93,88],[274,89],[91,88],[273,90],[68,91],[391,92],[392,92],[393,93],[330,94],[394,95],[395,96],[396,97],[328,27],[397,98],[398,99],[399,100],[400,101],[401,102],[402,103],[403,103],[404,104],[405,105],[406,106],[407,107],[331,27],[329,27],[408,108],[409,109],[410,110],[452,111],[411,112],[412,113],[413,112],[414,114],[415,115],[416,116],[417,117],[418,117],[419,117],[420,118],[421,119],[422,120],[423,121],[424,122],[425,123],[426,123],[427,124],[428,27],[429,27],[430,125],[431,126],[432,127],[433,125],[434,128],[435,129],[436,130],[437,131],[438,132],[439,133],[440,134],[441,135],[442,136],[443,137],[444,138],[445,139],[446,140],[447,141],[448,142],[332,112],[333,27],[334,143],[335,144],[336,27],[337,145],[338,27],[382,146],[383,147],[384,148],[385,148],[386,149],[387,27],[388,95],[389,150],[390,147],[449,151],[450,152],[451,153],[67,27],[279,27],[58,27],[59,27],[11,27],[10,27],[2,27],[12,27],[13,27],[14,27],[15,27],[16,27],[17,27],[18,27],[19,27],[3,27],[20,27],[21,27],[4,27],[22,27],[26,27],[23,27],[24,27],[25,27],[27,27],[28,27],[29,27],[5,27],[30,27],[31,27],[32,27],[33,27],[6,27],[37,27],[34,27],[35,27],[36,27],[38,27],[7,27],[39,27],[44,27],[45,27],[40,27],[41,27],[42,27],[43,27],[8,27],[49,27],[46,27],[47,27],[48,27],[50,27],[9,27],[51,27],[52,27],[53,27],[55,27],[54,27],[56,27],[1,27],[57,27],[357,154],[370,155],[354,156],[371,157],[380,158],[345,159],[346,160],[344,161],[379,162],[374,163],[378,164],[348,165],[367,166],[347,167],[377,168],[342,169],[343,163],[349,170],[350,27],[356,171],[353,170],[340,172],[381,173],[372,174],[360,175],[359,170],[361,176],[364,177],[358,178],[362,179],[375,162],[351,180],[352,181],[365,182],[341,157],[369,183],[368,170],[355,181],[363,184],[366,185],[373,27],[339,27],[376,186],[278,187],[325,188],[60,27],[64,189],[326,190],[324,27],[65,191],[74,192],[73,193],[75,194],[69,27],[66,27],[70,195],[327,196],[71,27],[276,197],[277,198],[72,27]],"latestChangedDtsFile":"./dist/tins.d.ts","version":"6.0.3"} \ No newline at end of file +{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/typescript/lib/lib.es2025.float16.d.ts","./node_modules/typescript/lib/lib.esnext.disposable.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./src/client.ts","./node_modules/@noble/hashes/esm/utils.d.ts","./node_modules/@noble/hashes/esm/_md.d.ts","./node_modules/@noble/hashes/esm/sha2.d.ts","./src/contracts.ts","./src/settlement-token.ts","./src/mempool.ts","./src/quote.ts","./node_modules/buffer/index.d.ts","./node_modules/@solana/web3.js/lib/index.d.ts","./src/program.ts","./src/send-estimate.ts","./src/settlement-economics.ts","./src/token-registry.ts","./src/payment-authorization.ts","./src/payment-authorization-server.ts","./src/payment-jobs.ts","./node_modules/@solana/spl-token/lib/types/actions/amountToUiAmount.d.ts","./node_modules/@solana/spl-token/lib/types/actions/approve.d.ts","./node_modules/@solana/spl-token/lib/types/actions/approveChecked.d.ts","./node_modules/@solana/spl-token/lib/types/actions/burn.d.ts","./node_modules/@solana/spl-token/lib/types/actions/burnChecked.d.ts","./node_modules/@solana/spl-token/lib/types/actions/closeAccount.d.ts","./node_modules/@solana/spl-token/lib/types/actions/createAccount.d.ts","./node_modules/@solana/spl-token/lib/types/actions/createAssociatedTokenAccount.d.ts","./node_modules/@solana/spl-token/lib/types/actions/createAssociatedTokenAccountIdempotent.d.ts","./node_modules/@solana/spl-token/lib/types/actions/createMint.d.ts","./node_modules/@solana/spl-token/lib/types/actions/createMultisig.d.ts","./node_modules/@solana/spl-token/lib/types/actions/createNativeMint.d.ts","./node_modules/@solana/spl-token/lib/types/actions/createWrappedNativeAccount.d.ts","./node_modules/@solana/spl-token/lib/types/actions/freezeAccount.d.ts","./node_modules/@solana/buffer-layout/lib/Layout.d.ts","./node_modules/@solana/spl-token/lib/types/state/mint.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/extensionType.d.ts","./node_modules/@solana/spl-token/lib/types/state/account.d.ts","./node_modules/@solana/spl-token/lib/types/actions/getOrCreateAssociatedTokenAccount.d.ts","./node_modules/@solana/spl-token/lib/types/actions/mintTo.d.ts","./node_modules/@solana/spl-token/lib/types/actions/mintToChecked.d.ts","./node_modules/@solana/spl-token/lib/types/actions/recoverNested.d.ts","./node_modules/@solana/spl-token/lib/types/actions/revoke.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/types.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/setAuthority.d.ts","./node_modules/@solana/spl-token/lib/types/actions/setAuthority.d.ts","./node_modules/@solana/spl-token/lib/types/actions/syncNative.d.ts","./node_modules/@solana/spl-token/lib/types/actions/thawAccount.d.ts","./node_modules/@solana/spl-token/lib/types/actions/transfer.d.ts","./node_modules/@solana/spl-token/lib/types/actions/transferChecked.d.ts","./node_modules/@solana/spl-token/lib/types/actions/uiAmountToAmount.d.ts","./node_modules/@solana/spl-token/lib/types/actions/index.d.ts","./node_modules/@solana/spl-token/lib/types/constants.d.ts","./node_modules/@solana/spl-token/lib/types/errors.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/accountType.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/cpiGuard/actions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/cpiGuard/instructions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/cpiGuard/state.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/cpiGuard/index.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/defaultAccountState/actions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/defaultAccountState/instructions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/defaultAccountState/state.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/defaultAccountState/index.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/groupMemberPointer/instructions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/groupMemberPointer/state.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/groupMemberPointer/index.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/groupPointer/instructions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/groupPointer/state.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/groupPointer/index.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/immutableOwner.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/interestBearingMint/actions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/interestBearingMint/instructions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/interestBearingMint/state.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/interestBearingMint/index.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/memoTransfer/actions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/memoTransfer/instructions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/memoTransfer/state.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/memoTransfer/index.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/metadataPointer/instructions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/metadataPointer/state.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/metadataPointer/index.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/scaledUiAmount/actions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/scaledUiAmount/instructions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/scaledUiAmount/state.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/scaledUiAmount/index.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/tokenGroup/actions.d.ts","./node_modules/@solana/spl-token-group/lib/types/errors.d.ts","./node_modules/@solana/spl-token-group/lib/types/instruction.d.ts","./node_modules/@solana/codecs-core/dist/types/readonly-uint8array.d.ts","./node_modules/@solana/codecs-core/dist/types/codec.d.ts","./node_modules/@solana/codecs-core/dist/types/add-codec-sentinel.d.ts","./node_modules/@solana/codecs-core/dist/types/add-codec-size-prefix.d.ts","./node_modules/@solana/codecs-core/dist/types/assertions.d.ts","./node_modules/@solana/codecs-core/dist/types/bytes.d.ts","./node_modules/@solana/codecs-core/dist/types/combine-codec.d.ts","./node_modules/@solana/codecs-core/dist/types/fix-codec-size.d.ts","./node_modules/@solana/codecs-core/dist/types/offset-codec.d.ts","./node_modules/@solana/codecs-core/dist/types/pad-codec.d.ts","./node_modules/@solana/codecs-core/dist/types/resize-codec.d.ts","./node_modules/@solana/codecs-core/dist/types/reverse-codec.d.ts","./node_modules/@solana/codecs-core/dist/types/transform-codec.d.ts","./node_modules/@solana/codecs-core/dist/types/index.d.ts","./node_modules/@solana/codecs-numbers/dist/types/assertions.d.ts","./node_modules/@solana/codecs-numbers/dist/types/common.d.ts","./node_modules/@solana/codecs-numbers/dist/types/f32.d.ts","./node_modules/@solana/codecs-numbers/dist/types/f64.d.ts","./node_modules/@solana/codecs-numbers/dist/types/i128.d.ts","./node_modules/@solana/codecs-numbers/dist/types/i16.d.ts","./node_modules/@solana/codecs-numbers/dist/types/i32.d.ts","./node_modules/@solana/codecs-numbers/dist/types/i64.d.ts","./node_modules/@solana/codecs-numbers/dist/types/i8.d.ts","./node_modules/@solana/codecs-numbers/dist/types/short-u16.d.ts","./node_modules/@solana/codecs-numbers/dist/types/u128.d.ts","./node_modules/@solana/codecs-numbers/dist/types/u16.d.ts","./node_modules/@solana/codecs-numbers/dist/types/u32.d.ts","./node_modules/@solana/codecs-numbers/dist/types/u64.d.ts","./node_modules/@solana/codecs-numbers/dist/types/u8.d.ts","./node_modules/@solana/codecs-numbers/dist/types/index.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/array.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/assertions.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/bit-array.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/boolean.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/bytes.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/constant.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/utils.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/discriminated-union.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/enum-helpers.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/enum.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/hidden-prefix.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/hidden-suffix.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/map.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/nullable.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/set.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/struct.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/tuple.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/union.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/unit.d.ts","./node_modules/@solana/codecs-data-structures/dist/types/index.d.ts","./node_modules/@solana/codecs-strings/dist/types/assertions.d.ts","./node_modules/@solana/codecs-strings/dist/types/base10.d.ts","./node_modules/@solana/codecs-strings/dist/types/base16.d.ts","./node_modules/@solana/codecs-strings/dist/types/base58.d.ts","./node_modules/@solana/codecs-strings/dist/types/base64.d.ts","./node_modules/@solana/codecs-strings/dist/types/baseX.d.ts","./node_modules/@solana/codecs-strings/dist/types/baseX-reslice.d.ts","./node_modules/@solana/codecs-strings/dist/types/null-characters.d.ts","./node_modules/@solana/codecs-strings/dist/types/utf8.d.ts","./node_modules/@solana/codecs-strings/dist/types/index.d.ts","./node_modules/@solana/options/dist/types/option.d.ts","./node_modules/@solana/options/dist/types/option-codec.d.ts","./node_modules/@solana/options/dist/types/unwrap-option.d.ts","./node_modules/@solana/options/dist/types/unwrap-option-recursively.d.ts","./node_modules/@solana/options/dist/types/index.d.ts","./node_modules/@solana/codecs/dist/types/index.d.ts","./node_modules/@solana/spl-token-group/lib/types/state/tokenGroup.d.ts","./node_modules/@solana/spl-token-group/lib/types/state/tokenGroupMember.d.ts","./node_modules/@solana/spl-token-group/lib/types/state/index.d.ts","./node_modules/@solana/spl-token-group/lib/types/index.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/tokenGroup/state.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/tokenGroup/index.d.ts","./node_modules/@solana/spl-token-metadata/lib/types/errors.d.ts","./node_modules/@solana/spl-token-metadata/lib/types/field.d.ts","./node_modules/@solana/spl-token-metadata/lib/types/instruction.d.ts","./node_modules/@solana/spl-token-metadata/lib/types/state.d.ts","./node_modules/@solana/spl-token-metadata/lib/types/index.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/tokenMetadata/actions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/tokenMetadata/state.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/tokenMetadata/index.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/mintCloseAuthority.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/nonTransferable.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/transferFee/actions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/transferFee/instructions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/transferFee/state.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/transferFee/index.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/permanentDelegate.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/transferHook/actions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/transferHook/instructions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/transferHook/seeds.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/transferHook/state.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/transferHook/pubkeyData.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/transferHook/index.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/pausable/actions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/pausable/instructions.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/pausable/state.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/pausable/index.d.ts","./node_modules/@solana/spl-token/lib/types/extensions/index.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/associatedTokenAccount.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/amountToUiAmount.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/approve.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/approveChecked.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/burn.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/burnChecked.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/closeAccount.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/freezeAccount.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/initializeAccount.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/initializeAccount2.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/initializeAccount3.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/initializeMint.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/initializeMint2.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/initializeMultisig.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/mintTo.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/mintToChecked.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/revoke.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/syncNative.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/thawAccount.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/transfer.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/transferChecked.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/uiAmountToAmount.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/decode.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/initializeMultisig2.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/initializeImmutableOwner.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/initializeMintCloseAuthority.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/reallocate.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/createNativeMint.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/initializeNonTransferableMint.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/initializePermanentDelegate.d.ts","./node_modules/@solana/spl-token/lib/types/instructions/index.d.ts","./node_modules/@solana/spl-token/lib/types/state/multisig.d.ts","./node_modules/@solana/spl-token/lib/types/state/index.d.ts","./node_modules/@solana/spl-token/lib/types/index.d.ts","./src/sponsored-settlement.ts","./src/tins.ts","./src/blockchain/solana-core.ts","./node_modules/eventemitter3/index.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/idl.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/program/context.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/program/common.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/utils/rpc.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/provider.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/nodewallet.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/error.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/program/namespace/account.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/program/accounts-resolver.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/program/namespace/instruction.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/program/namespace/transaction.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/program/namespace/rpc.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/program/namespace/simulate.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/program/namespace/views.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/program/namespace/methods.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/program/namespace/types.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/program/event.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/coder/borsh/accounts.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/coder/borsh/event.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/coder/borsh/types.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/coder/borsh/index.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/coder/system/instruction.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/coder/system/accounts.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/coder/system/events.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/coder/system/types.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/coder/system/index.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/coder/index.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/coder/borsh/instruction.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/utils/sha256.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/utils/pubkey.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/utils/bytes/hex.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/utils/bytes/utf8.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/utils/bytes/bs58.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/utils/bytes/base64.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/utils/bytes/index.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/utils/token.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/utils/features.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/utils/registry.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/utils/index.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/program/namespace/index.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/program/index.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/native/system.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/native/index.d.ts","./node_modules/@coral-xyz/anchor/dist/cjs/index.d.ts","./src/lib/logger.ts","./src/blockchain/solana-tsn.ts","./src/index.ts","./src/server.ts","./node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/@types/node/globals.typedarray.d.ts","./node_modules/@types/node/buffer.buffer.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/web-globals/abortcontroller.d.ts","./node_modules/@types/node/web-globals/blob.d.ts","./node_modules/@types/node/web-globals/console.d.ts","./node_modules/@types/node/web-globals/crypto.d.ts","./node_modules/@types/node/web-globals/domexception.d.ts","./node_modules/@types/node/web-globals/encoding.d.ts","./node_modules/@types/node/web-globals/events.d.ts","./node_modules/undici-types/utility.d.ts","./node_modules/undici-types/header.d.ts","./node_modules/undici-types/readable.d.ts","./node_modules/undici-types/fetch.d.ts","./node_modules/undici-types/formdata.d.ts","./node_modules/undici-types/connector.d.ts","./node_modules/undici-types/client-stats.d.ts","./node_modules/undici-types/client.d.ts","./node_modules/undici-types/errors.d.ts","./node_modules/undici-types/dispatcher.d.ts","./node_modules/undici-types/global-dispatcher.d.ts","./node_modules/undici-types/global-origin.d.ts","./node_modules/undici-types/pool-stats.d.ts","./node_modules/undici-types/pool.d.ts","./node_modules/undici-types/handlers.d.ts","./node_modules/undici-types/balanced-pool.d.ts","./node_modules/undici-types/round-robin-pool.d.ts","./node_modules/undici-types/h2c-client.d.ts","./node_modules/undici-types/agent.d.ts","./node_modules/undici-types/mock-interceptor.d.ts","./node_modules/undici-types/mock-call-history.d.ts","./node_modules/undici-types/mock-agent.d.ts","./node_modules/undici-types/mock-client.d.ts","./node_modules/undici-types/mock-pool.d.ts","./node_modules/undici-types/snapshot-agent.d.ts","./node_modules/undici-types/mock-errors.d.ts","./node_modules/undici-types/proxy-agent.d.ts","./node_modules/undici-types/socks5-proxy-agent.d.ts","./node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/undici-types/retry-handler.d.ts","./node_modules/undici-types/retry-agent.d.ts","./node_modules/undici-types/api.d.ts","./node_modules/undici-types/cache-interceptor.d.ts","./node_modules/undici-types/interceptors.d.ts","./node_modules/undici-types/util.d.ts","./node_modules/undici-types/cookies.d.ts","./node_modules/undici-types/patch.d.ts","./node_modules/undici-types/websocket.d.ts","./node_modules/undici-types/eventsource.d.ts","./node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/undici-types/content-type.d.ts","./node_modules/undici-types/cache.d.ts","./node_modules/undici-types/index.d.ts","./node_modules/@types/node/web-globals/fetch.d.ts","./node_modules/@types/node/web-globals/importmeta.d.ts","./node_modules/@types/node/web-globals/messaging.d.ts","./node_modules/@types/node/web-globals/navigator.d.ts","./node_modules/@types/node/web-globals/performance.d.ts","./node_modules/@types/node/web-globals/storage.d.ts","./node_modules/@types/node/web-globals/streams.d.ts","./node_modules/@types/node/web-globals/timers.d.ts","./node_modules/@types/node/web-globals/url.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/assert/strict.d.ts","./node_modules/@types/node/async_hooks.d.ts","./node_modules/@types/node/buffer.d.ts","./node_modules/@types/node/child_process.d.ts","./node_modules/@types/node/cluster.d.ts","./node_modules/@types/node/console.d.ts","./node_modules/@types/node/constants.d.ts","./node_modules/@types/node/crypto.d.ts","./node_modules/@types/node/dgram.d.ts","./node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/@types/node/dns.d.ts","./node_modules/@types/node/dns/promises.d.ts","./node_modules/@types/node/domain.d.ts","./node_modules/@types/node/events.d.ts","./node_modules/@types/node/fs.d.ts","./node_modules/@types/node/fs/promises.d.ts","./node_modules/@types/node/http.d.ts","./node_modules/@types/node/http2.d.ts","./node_modules/@types/node/https.d.ts","./node_modules/@types/node/inspector.d.ts","./node_modules/@types/node/inspector.generated.d.ts","./node_modules/@types/node/inspector/promises.d.ts","./node_modules/@types/node/module.d.ts","./node_modules/@types/node/net.d.ts","./node_modules/@types/node/os.d.ts","./node_modules/@types/node/path.d.ts","./node_modules/@types/node/path/posix.d.ts","./node_modules/@types/node/path/win32.d.ts","./node_modules/@types/node/perf_hooks.d.ts","./node_modules/@types/node/process.d.ts","./node_modules/@types/node/punycode.d.ts","./node_modules/@types/node/querystring.d.ts","./node_modules/@types/node/quic.d.ts","./node_modules/@types/node/readline.d.ts","./node_modules/@types/node/readline/promises.d.ts","./node_modules/@types/node/repl.d.ts","./node_modules/@types/node/sea.d.ts","./node_modules/@types/node/sqlite.d.ts","./node_modules/@types/node/stream.d.ts","./node_modules/@types/node/stream/consumers.d.ts","./node_modules/@types/node/stream/iter.d.ts","./node_modules/@types/node/stream/promises.d.ts","./node_modules/@types/node/stream/web.d.ts","./node_modules/@types/node/string_decoder.d.ts","./node_modules/@types/node/test.d.ts","./node_modules/@types/node/test/reporters.d.ts","./node_modules/@types/node/timers.d.ts","./node_modules/@types/node/timers/promises.d.ts","./node_modules/@types/node/tls.d.ts","./node_modules/@types/node/trace_events.d.ts","./node_modules/@types/node/tty.d.ts","./node_modules/@types/node/url.d.ts","./node_modules/@types/node/util.d.ts","./node_modules/@types/node/util/types.d.ts","./node_modules/@types/node/v8.d.ts","./node_modules/@types/node/vm.d.ts","./node_modules/@types/node/wasi.d.ts","./node_modules/@types/node/worker_threads.d.ts","./node_modules/@types/node/zlib.d.ts","./node_modules/@types/node/zlib/iter.d.ts","./node_modules/@types/node/index.d.ts"],"fileIdsList":[[281,307,331,395,403,407,410,412,413,414,426],[281,298,299,300,307,308,331,395,403,407,410,412,413,414,426],[69,281,307,331,395,403,407,410,412,413,414,426],[281,297,301,306,331,395,403,407,410,412,413,414,426],[281,297,307,331,395,403,407,410,412,413,414,426],[281,302,303,304,305,307,331,395,403,407,410,412,413,414,426],[69,331,395,403,407,410,412,413,414,426],[69,281,285,286,287,289,307,308,319,321,323,331,395,403,407,410,412,413,414,426],[322,324,331,395,403,407,410,412,413,414,426],[69,285,306,321,331,395,403,407,410,412,413,414,426],[69,285,331,395,403,407,410,412,413,414,426],[69,281,285,288,295,296,331,395,403,407,410,412,413,414,426],[69,280,281,282,331,395,403,407,410,412,413,414,426],[69,281,283,331,395,403,407,410,412,413,414,426],[69,281,285,296,307,331,395,403,407,410,412,413,414,426],[69,281,282,283,285,289,297,307,320,331,395,403,407,410,412,413,414,426],[69,280,281,283,285,296,307,331,395,403,407,410,412,413,414,426],[69,281,285,288,289,290,291,292,293,294,295,296,307,331,395,403,407,410,412,413,414,426],[69,281,282,296,331,395,403,407,410,412,413,414,426],[69,281,282,283,285,288,289,290,291,292,293,294,296,331,395,403,407,410,412,413,414,426],[69,281,285,291,296,331,395,403,407,410,412,413,414,426],[69,281,285,291,296,297,307,331,395,403,407,410,412,413,414,426],[69,281,290,296,331,395,403,407,410,412,413,414,426],[69,281,282,295,331,395,403,407,410,412,413,414,426],[69,281,293,296,331,395,403,407,410,412,413,414,426],[69,284,331,395,403,407,410,412,413,414,426],[331,395,403,407,410,412,413,414,426],[311,312,313,314,331,395,403,407,410,412,413,414,426],[284,309,310,315,316,317,318,331,395,403,407,410,412,413,414,426],[69,283,285,331,395,403,407,410,412,413,414,426],[61,331,395,403,407,410,412,413,414,426],[61,62,331,395,403,407,410,412,413,414,426],[145,146,331,395,403,407,410,412,413,414,426],[146,331,395,403,407,410,412,413,414,426],[145,331,395,403,407,410,412,413,414,426],[145,146,147,148,149,150,151,152,153,154,155,156,157,331,395,403,407,410,412,413,414,426],[158,174,331,395,403,407,410,412,413,414,426],[158,331,395,403,407,410,412,413,414,426],[158,174,181,331,395,403,407,410,412,413,414,426],[158,174,183,331,395,403,407,410,412,413,414,426],[175,176,177,178,179,180,182,184,185,186,187,188,189,190,191,192,193,331,395,403,407,410,412,413,414,426],[158,174,175,331,395,403,407,410,412,413,414,426],[158,181,331,395,403,407,410,412,413,414,426],[158,160,331,395,403,407,410,412,413,414,426],[159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,331,395,403,407,410,412,413,414,426],[195,196,197,198,199,200,201,202,203,331,395,403,407,410,412,413,414,426],[158,174,194,204,209,331,395,403,407,410,412,413,414,426],[205,206,207,208,331,395,403,407,410,412,413,414,426],[158,174,205,331,395,403,407,410,412,413,414,426],[205,331,395,403,407,410,412,413,414,426],[143,144,213,331,395,403,407,410,412,413,414,426],[211,212,331,395,403,407,410,412,413,414,426],[69,210,331,395,403,407,410,412,413,414,426],[210,331,395,403,407,410,412,413,414,426],[217,218,219,220,331,395,403,407,410,412,413,414,426],[69,218,331,395,403,407,410,412,413,414,426],[69,94,331,395,403,407,410,412,413,414,426],[77,78,79,80,81,82,83,84,85,86,87,88,89,90,95,96,97,98,99,102,103,104,105,106,107,331,395,403,407,410,412,413,414,426],[69,101,331,395,403,407,410,412,413,414,426],[112,113,114,331,395,403,407,410,412,413,414,426],[69,91,100,331,395,403,407,410,412,413,414,426],[91,94,331,395,403,407,410,412,413,414,426],[116,117,118,331,395,403,407,410,412,413,414,426],[69,91,94,100,331,395,403,407,410,412,413,414,426],[91,92,94,331,395,403,407,410,412,413,414,426],[69,92,331,395,403,407,410,412,413,414,426],[120,121,331,395,403,407,410,412,413,414,426],[69,91,92,331,395,403,407,410,412,413,414,426],[123,124,331,395,403,407,410,412,413,414,426],[93,111,115,119,122,125,126,130,134,137,141,216,224,225,226,230,231,237,241,331,395,403,407,410,412,413,414,426],[127,128,129,331,395,403,407,410,412,413,414,426],[131,132,133,331,395,403,407,410,412,413,414,426],[135,136,331,395,403,407,410,412,413,414,426],[238,239,240,331,395,403,407,410,412,413,414,426],[69,91,92,94,331,395,403,407,410,412,413,414,426],[138,139,140,331,395,403,407,410,412,413,414,426],[142,215,331,395,403,407,410,412,413,414,426],[92,214,331,395,403,407,410,412,413,414,426],[69,221,331,395,403,407,410,412,413,414,426],[222,223,331,395,403,407,410,412,413,414,426],[227,228,229,331,395,403,407,410,412,413,414,426],[232,233,234,235,236,331,395,403,407,410,412,413,414,426],[108,109,110,242,273,275,331,395,403,407,410,412,413,414,426],[69,101,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,331,395,403,407,410,412,413,414,426],[100,101,214,221,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,331,395,403,407,410,412,413,414,426],[69,93,100,331,395,403,407,410,412,413,414,426],[69,100,331,395,403,407,410,412,413,414,426],[69,91,93,331,395,403,407,410,412,413,414,426],[92,94,274,331,395,403,407,410,412,413,414,426],[69,91,331,395,403,407,410,412,413,414,426],[331,395,403,407,409,410,411,412,413,414,426],[331,392,393,395,403,407,410,412,413,414,426],[331,394,395,403,407,410,412,413,414,426],[395,403,407,410,412,413,414,426],[331,395,403,407,410,412,413,414,426,435],[331,395,396,401,403,406,407,410,412,413,414,416,426,431,444],[331,395,396,397,403,406,407,410,412,413,414,426],[331,395,398,403,407,410,412,413,414,426,445],[331,395,399,400,403,407,410,412,413,414,417,426],[331,395,400,403,407,410,412,413,414,426,431,441],[331,395,401,403,406,407,410,412,413,414,416,426],[331,394,395,402,403,407,410,412,413,414,426],[331,395,403,404,407,410,412,413,414,426],[331,395,403,405,406,407,410,412,413,414,426],[331,394,395,403,406,407,410,412,413,414,426],[331,395,403,406,407,408,410,412,413,414,426,431,444],[331,395,403,406,407,408,410,412,413,414,426,431,433,435],[331,382,395,403,406,407,409,410,412,413,414,416,426,431,444],[331,395,403,406,407,409,410,412,413,414,416,426,431,441,444],[331,395,403,407,409,410,411,412,413,414,426,431,441,444],[329,330,331,332,333,334,335,336,337,338,339,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451,452],[331,395,403,406,407,410,412,413,414,426],[331,395,403,407,410,412,414,426],[331,395,403,407,410,412,413,414,415,426,444],[331,395,403,406,407,410,412,413,414,416,426,431],[331,395,403,407,410,412,413,414,417,426],[331,395,403,407,410,412,413,414,418,426],[331,395,403,406,407,410,412,413,414,421,426],[331,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451],[331,395,403,407,410,412,413,414,423,426],[331,395,403,407,410,412,413,414,424,426],[331,395,400,403,407,410,412,413,414,416,426,435],[331,395,403,406,407,410,412,413,414,426,427],[331,395,403,407,410,412,413,414,426,428,445,448],[331,395,403,406,407,410,412,413,414,426,431,434,435],[331,395,403,407,410,412,413,414,426,432,435],[331,395,403,407,410,412,413,414,426,433],[331,395,403,407,410,412,413,414,426,435,445],[331,395,403,407,410,412,413,414,426,436],[331,392,395,403,407,410,412,413,414,426,431,438,444],[331,395,403,407,410,412,413,414,426,431,437],[331,395,403,406,407,410,412,413,414,426,439,440],[331,395,403,407,410,412,413,414,426,439,440],[331,395,400,403,407,410,412,413,414,416,426,431,441],[331,395,403,407,410,412,413,414,426,442],[331,395,403,407,410,412,413,414,416,426,443],[331,395,403,407,409,410,412,413,414,424,426,444],[331,395,403,407,410,412,413,414,426,445,446],[331,395,400,403,407,410,412,413,414,426,446],[331,395,403,407,410,412,413,414,426,431,447],[331,395,403,407,410,412,413,414,415,426,448],[331,395,403,407,410,412,413,414,426,449],[331,395,398,403,407,410,412,413,414,426],[331,395,400,403,407,410,412,413,414,426],[331,395,403,407,410,412,413,414,426,445],[331,382,395,403,407,410,412,413,414,426],[331,395,403,407,410,412,413,414,426,444],[331,395,403,407,410,412,413,414,426,450],[331,395,403,407,410,412,413,414,421,426],[331,395,403,407,410,412,413,414,426,440],[331,382,395,403,406,407,408,410,412,413,414,421,426,431,435,444,447,448,450],[331,395,403,407,410,412,413,414,426,431,451],[331,395,403,407,410,412,413,414,426,433,452],[331,346,349,352,353,395,403,407,410,412,413,414,426,444],[331,349,395,403,407,410,412,413,414,426,431,444],[331,349,353,395,403,407,410,412,413,414,426,444],[331,395,403,407,410,412,413,414,426,431],[331,343,395,403,407,410,412,413,414,426],[331,347,395,403,407,410,412,413,414,426],[331,345,346,349,395,403,407,410,412,413,414,426,444],[331,395,403,407,410,412,413,414,416,426,441],[331,395,403,407,410,412,413,414,426,453],[331,343,395,403,407,410,412,413,414,426,453],[331,345,349,395,403,407,410,412,413,414,416,426,444],[331,340,341,342,344,348,395,403,406,407,410,412,413,414,426,431,444],[331,349,358,366,395,403,407,410,412,413,414,426],[331,341,347,395,403,407,410,412,413,414,426],[331,349,376,377,395,403,407,410,412,413,414,426],[331,341,344,349,395,403,407,410,412,413,414,426,435,444,453],[331,349,395,403,407,410,412,413,414,426],[331,345,349,395,403,407,410,412,413,414,426,444],[331,340,395,403,407,410,412,413,414,426],[331,343,344,345,347,348,349,350,351,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,377,378,379,380,381,395,403,407,410,412,413,414,426],[331,349,369,372,395,403,407,410,412,413,414,426],[331,349,358,359,360,395,403,407,410,412,413,414,426],[331,347,349,359,361,395,403,407,410,412,413,414,426],[331,348,395,403,407,410,412,413,414,426],[331,341,343,349,395,403,407,410,412,413,414,426],[331,349,353,359,361,395,403,407,410,412,413,414,426],[331,353,395,403,407,410,412,413,414,426],[331,347,349,352,395,403,407,410,412,413,414,426,444],[331,341,345,349,358,395,403,407,410,412,413,414,426],[331,349,369,395,403,407,410,412,413,414,426],[331,361,395,403,407,410,412,413,414,426],[331,341,345,349,353,395,403,407,410,412,413,414,426],[331,343,349,376,395,403,407,410,412,413,414,426,435,450,453],[69,70,276,331,395,400,403,407,410,412,413,414,426],[69,70,276,279,324,325,331,395,400,403,407,410,412,413,414,426],[61,63,331,395,403,407,410,412,413,414,426],[60,64,65,66,67,70,71,72,73,74,75,76,277,278,279,326,331,395,403,407,410,412,413,414,426],[60,64,65,331,395,400,403,407,408,410,412,413,414,418,426],[69,331,395,400,403,407,410,412,413,414,426],[60,64,331,395,403,407,410,412,413,414,426],[64,66,74,75,331,395,403,407,410,412,413,414,426],[67,69,70,331,395,403,407,410,412,413,414,426],[66,331,395,403,407,409,410,412,413,414,426],[61,63,69,70,276,331,395,403,407,410,412,413,414,426],[63,69,331,395,403,407,410,412,413,414,426]],"fileInfos":[{"version":"bcd24271a113971ba9eb71ff8cb01bc6b0f872a85c23fdbe5d93065b375933cd","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f88bedbeb09c6f5a6645cb24c7c55f1aa22d19ae96c8e6959cbd8b85a707bc6","impliedFormat":1},{"version":"7fe93b39b810eadd916be8db880dd7f0f7012a5cc6ffb62de8f62a2117fa6f1f","impliedFormat":1},{"version":"bb0074cc08b84a2374af33d8bf044b80851ccc9e719a5e202eacf40db2c31600","impliedFormat":1},{"version":"1a7daebe4f45fb03d9ec53d60008fbf9ac45a697fdc89e4ce218bc94b94f94d6","impliedFormat":1},{"version":"f94b133a3cb14a288803be545ac2683e0d0ff6661bcd37e31aaaec54fc382aed","impliedFormat":1},{"version":"f59d0650799f8782fd74cf73c19223730c6d1b9198671b1c5b3a38e1188b5953","impliedFormat":1},{"version":"8a15b4607d9a499e2dbeed9ec0d3c0d7372c850b2d5f1fb259e8f6d41d468a84","impliedFormat":1},{"version":"26e0fe14baee4e127f4365d1ae0b276f400562e45e19e35fd2d4c296684715e6","impliedFormat":1},{"version":"eadcffda2aa84802c73938e589b9e58248d74c59cb7fcbca6474e3435ac15504","affectsGlobalScope":true,"impliedFormat":1},{"version":"105ba8ff7ba746404fe1a2e189d1d3d2e0eb29a08c18dded791af02f29fb4711","affectsGlobalScope":true,"impliedFormat":1},{"version":"00343ca5b2e3d48fa5df1db6e32ea2a59afab09590274a6cccb1dbae82e60c7c","affectsGlobalScope":true,"impliedFormat":1},{"version":"ebd9f816d4002697cb2864bea1f0b70a103124e18a8cd9645eeccc09bdf80ab4","affectsGlobalScope":true,"impliedFormat":1},{"version":"2c1afac30a01772cd2a9a298a7ce7706b5892e447bb46bdbeef720f7b5da77ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"7b0225f483e4fa685625ebe43dd584bb7973bbd84e66a6ba7bbe175ee1048b4f","affectsGlobalScope":true,"impliedFormat":1},{"version":"c0a4b8ac6ce74679c1da2b3795296f5896e31c38e888469a8e0f99dc3305de60","affectsGlobalScope":true,"impliedFormat":1},{"version":"3084a7b5f569088e0146533a00830e206565de65cae2239509168b11434cd84f","affectsGlobalScope":true,"impliedFormat":1},{"version":"c5079c53f0f141a0698faa903e76cb41cd664e3efb01cc17a5c46ec2eb0bef42","affectsGlobalScope":true,"impliedFormat":1},{"version":"32cafbc484dea6b0ab62cf8473182bbcb23020d70845b406f80b7526f38ae862","affectsGlobalScope":true,"impliedFormat":1},{"version":"fca4cdcb6d6c5ef18a869003d02c9f0fd95df8cfaf6eb431cd3376bc034cad36","affectsGlobalScope":true,"impliedFormat":1},{"version":"b93ec88115de9a9dc1b602291b85baf825c85666bf25985cc5f698073892b467","affectsGlobalScope":true,"impliedFormat":1},{"version":"f5c06dcc3fe849fcb297c247865a161f995cc29de7aa823afdd75aaaddc1419b","affectsGlobalScope":true,"impliedFormat":1},{"version":"b77e16112127a4b169ef0b8c3a4d730edf459c5f25fe52d5e436a6919206c4d7","affectsGlobalScope":true,"impliedFormat":1},{"version":"fbffd9337146eff822c7c00acbb78b01ea7ea23987f6c961eba689349e744f8c","affectsGlobalScope":true,"impliedFormat":1},{"version":"a995c0e49b721312f74fdfb89e4ba29bd9824c770bbb4021d74d2bf560e4c6bd","affectsGlobalScope":true,"impliedFormat":1},{"version":"c7b3542146734342e440a84b213384bfa188835537ddbda50d30766f0593aff9","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce6180fa19b1cccd07ee7f7dbb9a367ac19c0ed160573e4686425060b6df7f57","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f02e2476bccb9dbe21280d6090f0df17d2f66b74711489415a8aa4df73c9675","affectsGlobalScope":true,"impliedFormat":1},{"version":"45e3ab34c1c013c8ab2dc1ba4c80c780744b13b5676800ae2e3be27ae862c40c","affectsGlobalScope":true,"impliedFormat":1},{"version":"805c86f6cca8d7702a62a844856dbaa2a3fd2abef0536e65d48732441dde5b5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"e42e397f1a5a77994f0185fd1466520691456c772d06bf843e5084ceb879a0ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"f4c2b41f90c95b1c532ecc874bd3c111865793b23aebcc1c3cbbabcd5d76ffb0","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab26191cfad5b66afa11b8bf935ef1cd88fabfcb28d30b2dfa6fad877d050332","affectsGlobalScope":true,"impliedFormat":1},{"version":"2088bc26531e38fb05eedac2951480db5309f6be3fa4a08d2221abb0f5b4200d","affectsGlobalScope":true,"impliedFormat":1},{"version":"cb9d366c425fea79716a8fb3af0d78e6b22ebbab3bd64d25063b42dc9f531c1e","affectsGlobalScope":true,"impliedFormat":1},{"version":"500934a8089c26d57ebdb688fc9757389bb6207a3c8f0674d68efa900d2abb34","affectsGlobalScope":true,"impliedFormat":1},{"version":"689da16f46e647cef0d64b0def88910e818a5877ca5379ede156ca3afb780ac3","affectsGlobalScope":true,"impliedFormat":1},{"version":"bc21cc8b6fee4f4c2440d08035b7ea3c06b3511314c8bab6bef7a92de58a2593","affectsGlobalScope":true,"impliedFormat":1},{"version":"7ca53d13d2957003abb47922a71866ba7cb2068f8d154877c596d63c359fed25","affectsGlobalScope":true,"impliedFormat":1},{"version":"54725f8c4df3d900cb4dac84b64689ce29548da0b4e9b7c2de61d41c79293611","affectsGlobalScope":true,"impliedFormat":1},{"version":"e5594bc3076ac29e6c1ebda77939bc4c8833de72f654b6e376862c0473199323","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f3eb332c2d73e729f3364fcc0c2b375e72a121e8157d25a82d67a138c83a95c","affectsGlobalScope":true,"impliedFormat":1},{"version":"6f4427f9642ce8d500970e4e69d1397f64072ab73b97e476b4002a646ac743b1","affectsGlobalScope":true,"impliedFormat":1},{"version":"48915f327cd1dea4d7bd358d9dc7732f58f9e1626a29cc0c05c8c692419d9bb7","affectsGlobalScope":true,"impliedFormat":1},{"version":"b7bf9377723203b5a6a4b920164df22d56a43f593269ba6ae1fdc97774b68855","affectsGlobalScope":true,"impliedFormat":1},{"version":"db9709688f82c9e5f65a119c64d835f906efe5f559d08b11642d56eb85b79357","affectsGlobalScope":true,"impliedFormat":1},{"version":"4b25b8c874acd1a4cf8444c3617e037d444d19080ac9f634b405583fd10ce1f7","affectsGlobalScope":true,"impliedFormat":1},{"version":"37be57d7c90cf1f8112ee2636a068d8fd181289f82b744160ec56a7dc158a9f5","affectsGlobalScope":true,"impliedFormat":1},{"version":"a917a49ac94cd26b754ab84e113369a75d1a47a710661d7cd25e961cc797065f","affectsGlobalScope":true,"impliedFormat":1},{"version":"6d3261badeb7843d157ef3e6f5d1427d0eeb0af0cf9df84a62cfd29fd47ac86e","affectsGlobalScope":true,"impliedFormat":1},{"version":"195daca651dde22f2167ac0d0a05e215308119a3100f5e6268e8317d05a92526","affectsGlobalScope":true,"impliedFormat":1},{"version":"8b11e4285cd2bb164a4dc09248bdec69e9842517db4ca47c1ba913011e44ff2f","affectsGlobalScope":true,"impliedFormat":1},{"version":"0508571a52475e245b02bc50fa1394065a0a3d05277fbf5120c3784b85651799","affectsGlobalScope":true,"impliedFormat":1},{"version":"8f9af488f510c3015af3cc8c267a9e9d96c4dd38a1fdff0e11dc5a544711415b","affectsGlobalScope":true,"impliedFormat":1},{"version":"fc611fea8d30ea72c6bbfb599c9b4d393ce22e2f5bfef2172534781e7d138104","affectsGlobalScope":true,"impliedFormat":1},{"version":"f128dae7c44d8f35ee42e0a437000a57c9f06cc04f8b4fb42eebf44954d53dc8","affectsGlobalScope":true,"impliedFormat":1},{"version":"1ecb8e347cb6b2a8927c09b86263663289418df375f5e68e11a0ae683776978f","affectsGlobalScope":true,"impliedFormat":1},{"version":"1ce14b81c5cc821994aa8ec1d42b220dd41b27fcc06373bce3958af7421b77d4","affectsGlobalScope":true,"impliedFormat":1},{"version":"b3a048b3e9302ef9a34ef4ebb9aecfb28b66abb3bce577206a79fee559c230da","affectsGlobalScope":true,"impliedFormat":1},{"version":"919c3390f668961a43405b58df09de9195f2f3e536aa5b2a2abb48611f078f7c","signature":"6a630f0117cb72926c4655069e47797d5b459f96bc3be828acb40b83a404b3de"},{"version":"b0bf8f866d3c05dce6c2778455252391bbc3fa0e8c1675e78dcee8fab2e1dd96","impliedFormat":99},{"version":"675058f412cecd4e2c028e1a74aa34d5510ab03ed78dae712437890bb0aba6ba","impliedFormat":99},{"version":"cf6dc97686cc424e560bc9938f79964cccecd270ad144ac0ba85f2d8caa1115d","impliedFormat":99},{"version":"ecbc2bbf9e2752b7945187a8ef528229ae242589b39b2107d78ad7ab7292f878","signature":"da113d9ea66995300feea34d971171b8a1440542519b5da2f023bbc3a11780f8"},{"version":"46f886b79d6ac719fd65d4523a173a2f9b48c4f38c1cca560048b836ad32bba6","signature":"ff2ae6f6a7eb30586f3e16686cd9b726357b7d535042333af869dd3257bd646f"},{"version":"caa7f576b7305e9f95d3cadf8ae243c784ea4113e65baca5d8352cd70db2337e","signature":"287f0eedd97f4d2cbce3bf087a3e7501e6b2445e706e1c64b1f24b0d951b887b"},{"version":"406eac15f527f7ef8a17520ebab520af3a01c5cba546f495129cbdf0ceff949a","signature":"eda265ad66f026b17160885823f1b963303816eec58e2c61a13fa01daba2d52d"},{"version":"4967529644e391115ca5592184d4b63980569adf60ee685f968fd59ab1557188","impliedFormat":1},{"version":"354648085514ffc13bde79849f02ee20496ec3807b751e07aed2105e6848396d","impliedFormat":1},{"version":"982d98ad0e0cc3ddb7a61de24d4e29cbc6ef77d60311f8a6151fc620056a13e0","signature":"102cf8b4beee187a48c4a19b40ef322afab3814c2ada7188bbc153213befb930"},{"version":"0956d07ee7ba33ffe94ca03c68a509d2653f1c7d17e66db4fc01c2426ca080e2","signature":"cd0f2b04c58a6ce8b6ed71438602561cdbfd31e58167d2397825d5e3a72069aa"},{"version":"a877ba05545b2369540cbee8e571697de714d8900ef398848036bd3f6c8c8957","signature":"162d529e90c52a13fd59d4ec83fdd9503c6ab1783a946eac0ac40ba95a33df63"},{"version":"b22940c33e79cfc4a4a651072f542106440b63b82c3836031188fb22865de05a","signature":"2f18fcffde4f3e540f8be12e6f1e38fdba4c5937bf46dc72359e9e46ae18fa5a"},{"version":"022bb80a7f30653ba6aca185762c1f23bd040bd6812588dbf62c7a3b5d35f324","signature":"6680d1c9c0a51fe16251ddbf07a93df61cb212bc4194e8edb6bd6c63d2f26837"},{"version":"e361b3766556e8ffb41e294eb5baf322d042152a70a363923682f788969d9480","signature":"2d0b66eda93ab76ad7fba6f4ae14715e1b2bb1d2127326e74c577800917f0b0c"},{"version":"bf3d17f1381f402fb6a72bc123b3f723ffed4a26ad4bcbcd5a560bb339a7994e","signature":"f10a2db30fcd5702e911a3b260d575785f03fa27ee8c1343fbbe42f2dd271cd5"},{"version":"e3ba886aaac6533cc4b9b19846d4f950420997b9a61beaf907d15ea3290a9c40","impliedFormat":99},{"version":"9da3be1581103e0acf5f08ebc5ed24460322dfafb1ac69da014d5dc4a483a800","impliedFormat":99},{"version":"da6886dca4ee769cb1fa40757d9172a83368e361be7bef76ec3cf07e4dc5f8a9","impliedFormat":99},{"version":"8685c3d0ddfc9dc4e46a2004c2f2ec4d87efeb5f0ad6a53d48c0582d3ffe0761","impliedFormat":99},{"version":"aad2e723639c5d10e757430433edc63bc47c80a2771d5f29a47d72efe5ccbc16","impliedFormat":99},{"version":"9bc5e15c3e8d531d2bd30df972010e70666bd6bd4de57ea73c8a54c256f201cf","impliedFormat":99},{"version":"366b4a710a821e9d12a8ba59976cb619255351b1ed46ff2a7cb59e52e3c5efd9","impliedFormat":99},{"version":"2eab9bd13de1418725bffeb914252923dfe7737aed548da636d1f83fe86d90ce","impliedFormat":99},{"version":"52e27ca19a14551040f2d416cc461ffb66d1e580614c9ded60349457aa8dc32f","impliedFormat":99},{"version":"ff95ffb2c3a0c528ecdef1d4f9d0583b053c7b5b14fad0484ca24a10f3d803c0","impliedFormat":99},{"version":"efbbcd99bc7d919c901830d380d550f09d54fed91ba10d7c01fd637100523e56","impliedFormat":99},{"version":"4ff9e90fd09f201e27e286c42b2e39947a4dbffebe8b1e363c19dc24e7de0cbc","impliedFormat":99},{"version":"c908d519afbcec2451ef3236e1e60ff0864a20008bb362b1dc65faae0d4a209f","impliedFormat":99},{"version":"2f0b1a054dc679eff33ea5d78f38fb52be3a2828484a089345a5922ca015d4ff","impliedFormat":99},{"version":"92ad95e6220ab829c8f5cfca9be43e26e041a2922cde6e998782030d41c49963","impliedFormat":1},{"version":"4efb7b1f86f666d3ccc0980950fbfd47433853ba0279335ac3a448de52e6ff0a","impliedFormat":99},{"version":"4c3d23ed1f0ae934ce1c70036bb86432853203a3c9bef65b0508cabe78bc8471","impliedFormat":99},{"version":"bce555ca7b622803e6d02fd5b329b8f9a1d210ce50170f7d9dc159850ee12289","impliedFormat":99},{"version":"def16e36d2c06f660bfd5b1475124076951f96be19432806b75fbb14fcf3585b","impliedFormat":99},{"version":"43ee8c4a935c01726fab0b7b9e4b6bd3691fb209fa621791f70aa630d55166c0","impliedFormat":99},{"version":"86ae83eb226a1b8e2f70de4a5610ac500ce8971b604632ce7bbcaaf3d13d399d","impliedFormat":99},{"version":"7f6ab1efc3c2fc908ed9c93f84f68a582d9167f60937235f2cd301478d7f5e94","impliedFormat":99},{"version":"b2751a77782a0dd713289c2e086699290f993a2a6ecf39958e4b03f680b31e21","impliedFormat":99},{"version":"b3c1319feedffe7321c34b7167042ae4667b1f2cbe75c12b2665ee81ee7269ef","impliedFormat":99},{"version":"b3d81d996ec2281f859a45eb5c16df03ca64a451ea9e46dbd1f7f5bb8f0d8aa3","impliedFormat":99},{"version":"09ab28987b85ab03c2fdc78a76e48604af3c048577f4bad6948b76f8701b5827","impliedFormat":99},{"version":"966042450d726d12936da89c37879161803fe69872edf798648c31c771ca7656","impliedFormat":99},{"version":"b1ed373950c740359a135801f40b932d7f28b3fb0dba2533048d6929bdb4bf64","impliedFormat":99},{"version":"908cc3d2610b39fca4286537b475d45d956a5ac81ecfac3b36509764b4c4e68e","impliedFormat":99},{"version":"573707d1170f4357c0e62c92913db845f7aa06612da1ef594790f24407efdb75","impliedFormat":99},{"version":"fc9684e27ee9e97daee7c4baf3426d733b6e9b269dc95090c07f60bc7c7241d8","impliedFormat":99},{"version":"bdb8af267942f3ba49df1ae5bf374e7fef80d6228301a5a14ea86b8f46419a97","impliedFormat":99},{"version":"e4c4a481968f01ca4caccc4775100deb9d7c13ea9c8cad905320d8a6eb882cc8","impliedFormat":99},{"version":"1a06351c322f26ea390b5f64be6bcb4581487c56295ce7910d26fbc145c0de09","impliedFormat":99},{"version":"8ea2f6db6a13c15cd18354dc171172bd25f69fafa38e51115e7e0d73fe0e7194","impliedFormat":99},{"version":"f09fb4fd37cad325148837d5af245b810361dea1dfe3b9295ea26811641ef9c5","impliedFormat":99},{"version":"ab1ca5724fd1834ea88fc64059e496b9b81d79587879b2542dc95629cb476df6","impliedFormat":99},{"version":"1a1cf67d17bdf51b03e5738d6691791d207023bb983880cfa40e694f4c7c3658","impliedFormat":99},{"version":"dbbece8a54c84cc9a24ef369d0f61b420438f0e4a49e1cb52f6a08466fd2f2cc","impliedFormat":99},{"version":"ec4546150cc5e8289a46866e5f09468ab9f8bd0e62388307dd8278b72753f124","impliedFormat":99},{"version":"3753072c118bb577a833b2beaa6083855150899d6e9e8808f82f8c7a0c897de7","impliedFormat":99},{"version":"ecbca823128bdadfe66e895f024554ed9bb30c0557ea7d0d76840d59fa9afb58","impliedFormat":99},{"version":"dbbece8a54c84cc9a24ef369d0f61b420438f0e4a49e1cb52f6a08466fd2f2cc","impliedFormat":99},{"version":"36554ee1a2f2320d303fab7ca4b29596bb177ffe548ca8b55e5bf84f93d3470f","impliedFormat":99},{"version":"1fb315481ddd56f10db8aa5e13d692d0de320d90dd1ec681838e240f514fc8a4","impliedFormat":99},{"version":"51bbbda01d321417d621a1d5be7d4e84a27c5cc5714045e6497af440bcf936ec","impliedFormat":99},{"version":"859af863983c2fb4b676c31cfd6644058b3a2ec60dcca7afec8545a1c18bf044","impliedFormat":99},{"version":"e4abb4ac717aa2536da992f1474028555d4cb6fa58f18ccbd7c85df091343511","impliedFormat":99},{"version":"51bbbda01d321417d621a1d5be7d4e84a27c5cc5714045e6497af440bcf936ec","impliedFormat":99},{"version":"0e028e080ee6c91ac3e9bc2e7811328fecf16478aee2744196e4dba5c63a5812","impliedFormat":99},{"version":"26cd4771b2758fab625c1e4daa0255373c4217c7c2f52b9f8cee614650e395b5","impliedFormat":99},{"version":"ec9265f91fc03169f575e783fdc7eb9b91631fc9508ebc4d72b5c7376dc2838f","impliedFormat":99},{"version":"f8212c6b11820840c05fe818464cecb86d913a56efbc96366cf15eaa1fa61ea7","impliedFormat":99},{"version":"dbbece8a54c84cc9a24ef369d0f61b420438f0e4a49e1cb52f6a08466fd2f2cc","impliedFormat":99},{"version":"df705626180117f2093223856259585b3980d4c76677f86ef056be64eaf8ef10","impliedFormat":99},{"version":"5ae0421ff74c7f92f2ae6ef99a4e5a4d08cd2ab7b9035ca8dc9094f87ec00854","impliedFormat":99},{"version":"2b26e0da909c8887901e6918a600b041b8a4187e3fc1d6a97d4327cbf9d78e20","impliedFormat":99},{"version":"dbbece8a54c84cc9a24ef369d0f61b420438f0e4a49e1cb52f6a08466fd2f2cc","impliedFormat":99},{"version":"1834b47b5e9e94d65d3dbc92eb741e700fc0295afa6bf6bc9e295d85b259510e","impliedFormat":99},{"version":"a986bf4e386590ddf9e1f2ddebec670d6cfffc7cb742e88d9cbf25d8cf6c6e3c","impliedFormat":99},{"version":"51bbbda01d321417d621a1d5be7d4e84a27c5cc5714045e6497af440bcf936ec","impliedFormat":99},{"version":"0950450a9b9e60d9b2d1c6655caf0c2ec1281ddb3bed8e43c97484cfd2529cdd","impliedFormat":99},{"version":"7bb22b0e03ee4cb0ec7018ece9b4a7d1b99947ec5ce303c41eebc18427d47014","impliedFormat":99},{"version":"c02b653b1114928953e8e073fec7ba7dc07b3c09fe0fbe177b75ccf715826b2a","impliedFormat":99},{"version":"dbbece8a54c84cc9a24ef369d0f61b420438f0e4a49e1cb52f6a08466fd2f2cc","impliedFormat":99},{"version":"92e884e9398fd2f92eeb89b453527e103bd4f3522c3fcfe9b37aa2f06cb55e59","impliedFormat":99},{"version":"00a1ef46b9c5cf7cef36f691a235c0cb6e04e7953548507d66f8b7d6c76b78ab","impliedFormat":99},{"version":"d412bee4d7cce20207f126da114e0fbcdf14ae37fc72b038b993aa7d2ab90501","impliedFormat":99},{"version":"dfd85bb9018f85a16f56b2bdb06712550c72ad43771c984f0740933562716b9f","impliedFormat":1},{"version":"3e15b8234de578c958c85af5df2d74728382abdc9b8e3a09f65a2f87908dda4e","impliedFormat":1},{"version":"9f237d2411854489c7f25dbef70faf7e1b4b8c97980b1e1519de9c298093a76b","impliedFormat":1},{"version":"8899191137ddd594c771a77dbb8769b2fae098882fcb1b63e19b32cbbbb147a4","impliedFormat":1},{"version":"ab6eb60d22a5cae7ea878c3b31706dbf7f25541abc65277b42d599b3d51e6223","impliedFormat":1},{"version":"3d69c83ee1b82b7bd7124b932bdf81870d33b8e4b902569943a977853ffe7d9c","impliedFormat":1},{"version":"0ddfb5bc655babbb14fd4209c7046df88373117d4b5bdbbc1ce7bcf36dcf3bb1","impliedFormat":1},{"version":"f3d2623af0d17114598a59ea2d028636fa457b8b403a182b9836c55c92316f08","impliedFormat":1},{"version":"7df556f9fe0b9c12d556e938d6f1919dc19309fe14b148b37e499b78f61c77c4","impliedFormat":1},{"version":"781a2bd6a5143d55c8f12566093eb77ec7cde12aa1f5e463ea25fdd56e27044b","impliedFormat":1},{"version":"86414c1a9abe962df3589f780ec79573ac7e387f93945d954f98ef4082c36872","impliedFormat":1},{"version":"5f8a909527729ed82721c24c937d53c0d7f043f9ee5c4f9b3d79b083bc5c29cd","impliedFormat":1},{"version":"96ab1f14df41476d899aa51f5ebb6c6cc73da6d4c453b8f5e028e6b97d5d4978","impliedFormat":1},{"version":"26ab229375d893b57a6beb1c7bf34bdb10f44ba014f540552ec85fd12934bcf7","impliedFormat":1},{"version":"38d27d13b6e4f4a71a3e56d7d328483d545292a113226c2868c3f805bbfc1634","impliedFormat":1},{"version":"8673dbde34d0cc05894df27e4bdf5990df492c4d34c3dcd18b6e21796fd15fe4","impliedFormat":1},{"version":"d670fb759fe85972ee913251ece82ef8316ad60db7da6ebd00870bb92c508fb1","impliedFormat":1},{"version":"c9bbd1795b24b0f7812e67af7d19c67ae939e8cd1d0c3f4a1317a3f6170c99f0","impliedFormat":1},{"version":"621e56640e255a3115583c4d6312570b42817fd94dd6c06648870c1346e2b1df","impliedFormat":1},{"version":"2371fcefbf57c50dcd79fea7bbd3ccc54ec0d2abe1de504874d4efa541883f76","impliedFormat":1},{"version":"33ab90858fc316775a8ecf48a01bb79f03817c3d96bf513c09ce1b8920e4dd76","impliedFormat":1},{"version":"a231a096989e067acee6dd64155bb1e53934a61df42d6aeb99a17807c9926ad0","impliedFormat":1},{"version":"0b1d800eaf4e727eb9c714fae8fd95e55fbeaf25857b49bea790af1574be4a85","impliedFormat":1},{"version":"e59528ccbc75b696898856a059587a882067d990d76745d64dbab1e6b4398009","impliedFormat":1},{"version":"252741724c8a904d920131b9a62208da4e9799cbb144283f3545a16accb1674f","impliedFormat":1},{"version":"b39d89bd47d82b44feb9d7d435e3a55ec57c1f6cc8d2bb1ca7864b2b99bc5bc9","impliedFormat":1},{"version":"38bfe4a8dfda39d360af47539b98baa383e3c79e350e3c55a978ce956d8ed4b1","impliedFormat":1},{"version":"0fdda965c835ca890a998a10ddf96c494e82b0c12eab67ca2230f68ea093c908","impliedFormat":1},{"version":"cf4df17dcb70eee873ad6d67cfcdbffe09772b627eb98b9432377fc75ef7092b","impliedFormat":1},{"version":"965417cee04e9d38845d413e467290774dcfd7ea81423e6fca44ca3e20997873","impliedFormat":1},{"version":"4c3695b79025f04f54e74536893918730bc10eb01e00b9c2f0baad00aa2dd89c","impliedFormat":1},{"version":"873c7dd4941cc43a45ee6f02012fb8344caaf996173df73a4e19913b1141900f","impliedFormat":1},{"version":"2531cd5e023c45796ff9996f859a5cbdda14259c5dbfbe7ed52c2cd6307e6667","impliedFormat":1},{"version":"5e5e719a24e3c5b53a7540394e92e97dab206620c00229202dc9f9fd106bce33","impliedFormat":1},{"version":"523a395a32c17b2fd93560fe91ca5b0ef40b475319a1f5e1a3a1a0a16632abd7","impliedFormat":1},{"version":"0f2022020e9b48d7d3cd73d78de09d33c7a02cef7fa9176a89df855347e98ec1","impliedFormat":1},{"version":"25f0fca6b5e0b89074334e87d8061b3c08293bf0b6dcd478fff7c695b99ca3cc","impliedFormat":1},{"version":"3527f58048baa0dbbb289945ea633f8cd10b5668d9c2d29641cab2b36791df37","impliedFormat":1},{"version":"de453fbd0c1cad55c59bb10e8b3f46e7fec4bf27a8149bd052a67bdf326b97c8","impliedFormat":1},{"version":"23aabcfed54fee592a22c01224811f6ab07b3f6e5242179e8f4de147b4b66992","impliedFormat":1},{"version":"8153edd1581568d8c35ea458627a87db312dfaa86e86957e0c6506f80c90d031","impliedFormat":1},{"version":"d2d33a12a2d340da5aae54a09c1af641b4c1c5d11c10af08e40a752697ff93c9","impliedFormat":1},{"version":"621c6f56254c53e61b975c1d7794cdce092b8c058bda96ab44e7d34039915724","impliedFormat":1},{"version":"24b2524c5d0caf2f9783afb8b5c7910bb688accdc50f0f1fd09d576769ad7056","impliedFormat":1},{"version":"4ea8fbe2c5dbd1e93e5be1805e950a8aefaf9ed8bf742624ef9bbead0548cc33","impliedFormat":1},{"version":"ceef64943b889ede63bea25d79c237bbf131d149bbbdb63d960afbe4e88394cb","impliedFormat":1},{"version":"f8dbaa1df3053e1e866be6335bd0bbfb59b087100c88280e173a7f8c71489f39","impliedFormat":1},{"version":"d4e51e1e64ba79db6a4287f4be2745f699d47c4cf4b561d44d0a5739604bbf2b","impliedFormat":1},{"version":"380c4123ec4b23e7ba369c0cdb3a53cc3ad5362d0798dac28eea37dccfa2831c","impliedFormat":1},{"version":"29f80580c3ef7d5985c77e92a43e9dcda01404a3e932b3c13ad0c81bed0604b0","impliedFormat":1},{"version":"9eea219d6aee0b2828d05390fca2d97897f28dfcfc9ea28e89e49b17e181fd86","impliedFormat":1},{"version":"e822c6d5791504716b5c354f34c6359f4eb5dc58b02eeb7ca2bacaf4e12964e1","impliedFormat":1},{"version":"68c0b30766379c66ec6c29f2f28a4ef812765f16bfd8179cd3e55044e7fbc1ed","impliedFormat":1},{"version":"f6cbc3623702fbac026bd009ae0487d71929794c1abce8078caf6cacc7a29284","impliedFormat":1},{"version":"df97caa5b48b8b45ea863dd2b4520c0314c22022f20520a8733d906b5af696dd","impliedFormat":1},{"version":"f5dd94c10a58d2f2bef1d5f0bb2dc74e2d4437b0f1715b8a350756fd9f18ebcd","impliedFormat":1},{"version":"ab42549112b159f45e982a5e510d09d47ad6d698e1e3883126cb2c32502c473b","impliedFormat":1},{"version":"00165eaa48e183fb416419d196455e68a87fe91062b8c00131fce8c89f44f8ef","impliedFormat":1},{"version":"5810d9853732cacc825eed5b17a78e6690e62d25f0bbd8007836e67d1fa86e6c","impliedFormat":1},{"version":"f88965900f3eac9928f67bf05e5eff94aa36fed8a755a4ae2c24742053172094","impliedFormat":1},{"version":"eb80a37e22e70eef871f80cd04b190d8bba835f1a6096040d947fe55d62a7db9","impliedFormat":1},{"version":"d969b99d2d72b2d4739109e9c9b9f7c49d070d672f65b47f043903d4bf708845","impliedFormat":1},{"version":"dad944020c14c58d2dde4da2affd973a84f9e3e4d5101144ca6b1d658edf6283","impliedFormat":1},{"version":"e0eecfdf06504177423ea604c5b7036aea5c05cff8e6b5c5bf65e09546668e39","impliedFormat":1},{"version":"9ed6d88f27fe2e8fcfd9a4cbfe5c950b212baeba9accb6d83666600addd4dfcc","impliedFormat":1},{"version":"6f4ee252d6e45ef593d7b0f6cbc424db48c2aa0b864b0fa70a6c075bcab0a02e","impliedFormat":1},{"version":"9152e9cb89776f3cca3aea54a1ffd516d0d7e985d2dfdd68dcdb4f7e5b9a9b71","impliedFormat":99},{"version":"ef0404706ce91746197fd5d39f7e3c4e92ad7bb83434d95d2cb29a4d549c7cfc","impliedFormat":99},{"version":"87a82461d9c74c259975943afba224f174b6929b8b69f19316f594029b110dd9","impliedFormat":99},{"version":"51b42b886fcf0a19b9ff49868f4e6a6c43bb63c3adbeca84ea5a4f2ed4d5138a","impliedFormat":99},{"version":"1825792255e342f5af15246fc9619e3759af6be25a4cc968999bea915e463a45","impliedFormat":99},{"version":"063c5946311513d1719f294be42441c737e304df8c0cf7faa2a8a68bc59cbdf5","impliedFormat":99},{"version":"cbd4af10b152134ef5c1ec62fc2e45b008eab6c6fea2f63abe8e60a5965ea73e","impliedFormat":99},{"version":"bee7e164f33a358aaf7d2c8d33fb2c57ad5ea0b4472f23aa29de0758a721b54f","impliedFormat":99},{"version":"ca5f5d0a3cae743b58dcbad8f67d45b46387e4677a971587dfab16a748d55a31","impliedFormat":99},{"version":"c2cf729834036adabccce9347584c8ad37e5950825b72c671df91578e7282ed1","impliedFormat":99},{"version":"48fe338e450cecf8f60b99b32ec056464690e0881a708623067295217461e584","impliedFormat":99},{"version":"9d31a8af6bc7c4b99b655f057cc1f07343fe9616bf794175ae4b6173743cc508","impliedFormat":99},{"version":"ea7feac90d8512b4f2ff6e1b4efb4367aa4a22912c356d6d98bc7f8a858b6dbb","impliedFormat":99},{"version":"063c5946311513d1719f294be42441c737e304df8c0cf7faa2a8a68bc59cbdf5","impliedFormat":99},{"version":"ac424eae6846b72fe5f0d5f699d198cb8aeae735924d0ea2ceb7a314e9eee4a1","impliedFormat":99},{"version":"9d4b95ab06738e0127a372f236d9b2428598709094548d63f51a8478b0253e2b","impliedFormat":99},{"version":"afe59e36dbf265d69eb9ef811fe4f15f4999570185587a7fb3aa741254c3ecd5","impliedFormat":99},{"version":"323f44e24829feb36b18b0d77c13d2dbcb5813fcc80ba2c5a6235f17741cff7f","impliedFormat":99},{"version":"178728484e7a6c10ceaaaa8e4ccca3c3dbe1c4c0dc74a9b101036f9ab0bdbcb0","impliedFormat":99},{"version":"dbbece8a54c84cc9a24ef369d0f61b420438f0e4a49e1cb52f6a08466fd2f2cc","impliedFormat":99},{"version":"1d0567a40b032ad0d8ef91ded14739dd78230d9a79b209a038d9aa74b49cfaf9","impliedFormat":99},{"version":"68465a68993beea899574bd899cb5d8fc7e1189d716bff6426836c49ff2c76d9","impliedFormat":99},{"version":"59263852a2516cf57e49d545fd52e923359624cba5f8cf595f48ae404fdf7524","impliedFormat":99},{"version":"1151f7b54019b6eff96526ec72d542274bd73632d473486d6fecd8084e53201e","impliedFormat":99},{"version":"edf122f1acbc0843190dc272de1ac542c77943477a8ab6110574cb632b7e1b01","impliedFormat":99},{"version":"ef01b6992842eeb7c25e1807e6d3936f134f498e5f030877e37340221800c501","impliedFormat":99},{"version":"726dd160da57c905467702b85d53263d92b3a87d5a05b289f0acd2b98c000c55","impliedFormat":99},{"version":"21312a463d81457867895b35095cfd827ec775142e5e113071c4714d24d45b39","impliedFormat":99},{"version":"6fc5b8953b8fb338a0839bb9e2edfdf6b80e6a14b169b54c5ec94899f4b6898b","impliedFormat":99},{"version":"7937d37b8615603f403e19a9b0e4357a214fc646fe4f50a7ea96f35aa90fb74a","impliedFormat":99},{"version":"dbbece8a54c84cc9a24ef369d0f61b420438f0e4a49e1cb52f6a08466fd2f2cc","impliedFormat":99},{"version":"c762aeec152965fb0de7e6c5cc500df1ab788acd86891488a6b1e39b782b1cc9","impliedFormat":99},{"version":"988e41d18cfb7249f367afaa04c1ef20dcee125b24b0ab22fa729ffadb302855","impliedFormat":99},{"version":"9d2679c417dffb67d7f5e8b1a29909fdeef58dfccff9a2bd3ee92bf7c9417b04","impliedFormat":99},{"version":"1ace3b57c44f6b67bbecc38bcd1001f4cdbe8cae9f2b1b682c5b723c5a396265","impliedFormat":99},{"version":"644979a865433b3107134325fabe637c2664878a5045170455a12e958426ed53","impliedFormat":99},{"version":"4f54c7fb5961a99ec9e3903f7c36705c8924bf8aa1b2101a6a53ef633b38ca4d","impliedFormat":99},{"version":"eb3b31f11e913d8efbe4b1c9a731c0750eba9e8ac7c64f961fe2b8747a0d8b55","impliedFormat":99},{"version":"d60d7172b4d4708afc0ec411eeb6ae889e8d87c25baae64cd70a8f77f003d751","impliedFormat":99},{"version":"106c47488674753b587c532bba6980d1010a1440c47ce25bd4ee9d65c4fcf9cf","impliedFormat":99},{"version":"f25408ad90b9a433f4124f2408ec5d71985514dcb25da5146077824d30a9262d","impliedFormat":99},{"version":"61c0ca22963ab7a232e6ff1f0e7558a3d90cbc489288bf5c2bceeb374196bc2a","impliedFormat":99},{"version":"3f19ba14366c8314024981eff375a3c4c82b5e8a1571f99021a2c595a58b7787","impliedFormat":99},{"version":"381feb39bec29f74e9a8762914cc44302fd5bb28f4a4167d19a08d0a2e6fbbc9","impliedFormat":99},{"version":"1e13042b06c91a57e17cadbf39df975995858b313efb976a17f03b1ed4ac941e","impliedFormat":99},{"version":"1bf7f34549f798ce83855e7852f40fe1b161a0a99cc2ec710fd3033b6c708efa","impliedFormat":99},{"version":"ffa11a6abf9bec5461a52f9540573d9587f5d5cfdf44e02ad69349004aeb0bf7","impliedFormat":99},{"version":"228780797f100a9097434e10e0a8881e9da01c40cd10d924c36ec3d29be99cd4","impliedFormat":99},{"version":"ad6597894af9ed66e55c285cb151132cf881c6a5a66550ac014c30d64654fb52","impliedFormat":99},{"version":"4c46080a788a4f839fd6e79089994bbc88b96a4e4223a9bf8f29bfa709e12609","impliedFormat":99},{"version":"8a33af7d173a56d3cfa0fd4468262da3d3ddc7cf56116ae0025b69fa0b4e5d5e","impliedFormat":99},{"version":"ba9b62f2363f96b897ff129d9186cf55ea6a03a8a680cb4797343c34232efce7","impliedFormat":99},{"version":"efcfadc996b81902d8255b8fee04921ad6ef19bc5d870c24661bfc977fbced8a","impliedFormat":99},{"version":"e1f4d2566bc0f54666b9d2f2898551872a3ad4e50de55d7caaa22614df8dc969","impliedFormat":99},{"version":"309ad3b88b4421dbe229aeea35ff3dffce9422e98f3e36da382571ccb497d0e6","impliedFormat":99},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":99},{"version":"79b03a391d27ebbcc950ee8e7d78fe638c7d4ec7a4080e6dacc9f91f841fec8a","impliedFormat":99},{"version":"23f0e9d8fabba6af02f11c4941f9a2ce03eca49a9de8366472ccf0bdb24022c5","impliedFormat":99},{"version":"afb0b71ed7867fc32b57f7d71b7a39bacc3c2d88cac6901a2eaab0e8197c3f2a","impliedFormat":99},{"version":"e513da1a3ebfdd6862962fb5fca869ad3f71de6ba8af5b55496953f1e363f224","impliedFormat":99},{"version":"f54fcff687473488b9e395e102d51ab88d474bada45f0356cbab6ad41779c33b","impliedFormat":99},{"version":"33c72ffe8e3337d0094324ff7d927ff660e4a12c5f0ee4aef3f62665d27574d5","impliedFormat":99},{"version":"c618ab843c5d3e5b80f7fff99894f434689a83aef4bbda2e2ff53530060dc20b","impliedFormat":99},{"version":"722f091a793140b7a17842b4514c3562c7ebaceb7408a3bf2e5e48faa719ce45","impliedFormat":99},{"version":"c7a5628418ad6e46974eb6247b5c66012ce5c8294d7461c01b5e9249cf8d9dc7","impliedFormat":99},{"version":"7a985ca144078c55ff33db714ae38156421c632efc598d65edec6c8ead046eb5","impliedFormat":99},{"version":"5a969f536f50d52ffa08c04e5b5d02da7a3233d10ebfd48e98d8eefc3a24bee4","signature":"1e9d9700fa21e1e3480d956cb174cfe148538600756f98d9c0af5c7f2b41e989"},{"version":"da2bfdfb44dd4b64a8232c394f976166251ae15e8be86a0c1702e4af6aac4b64","signature":"1a6c23ea651200f52e4cf2306d7c2af89cc4bba38168be39c63b6e64a9f6d8d2"},{"version":"d63687bdf4f7498820df014e95353c3bc1159d28c4732c2aaeb0dd341aec1758","signature":"e6651ac2fb47f61c8e52f91bdd294aeefcd02da3013ed4ef87068a6930f88486"},{"version":"b80c780c52524beb13488942543972c8b0e54400e8b59cee0169f38d0fabb968","impliedFormat":1},{"version":"843772e418a5fbe2d062316490eeb0349ce9513a0773ce751fedc9d435f2fa0f","impliedFormat":1},{"version":"14dea907a1d49d236e77b00a0c1968b8ca1f6c28fd9fbc90e515573090ce255c","impliedFormat":1},{"version":"42e8d611a11ffaeb683e12f1c913b6cb35b6745f15976efbef313cdd187143bb","impliedFormat":1},{"version":"82a08eb132e8a6e5d9a40f211549af26aa7d08cbdcc3a8de8c785f8b3a61e1b9","impliedFormat":1},{"version":"e5e7bac8ba2daab62e2d07c8c350d63f01dfe32af4b0b1ce683bf792f3ecd0f7","impliedFormat":1},{"version":"4d0f5e6f876b9942c224d9677b03cdbce1504551671db4e4e60a4834ffa9dda0","impliedFormat":1},{"version":"3bec87611d4741499b645af03bba9a41e7519ae855ebcafb3db244c21b907e4f","impliedFormat":1},{"version":"e63efe908892e6a7f09c8b97ead3440d7da737b848daa0a577ae667173445ead","impliedFormat":1},{"version":"4c8533a16f6c4597d9fc7c0c7f21a3908084a819a111d5637ff2defa7b345bfe","impliedFormat":1},{"version":"57b41a36f36758b39c7d0402350ca4971dedeb3c2a63cecdac060a5320c04c55","impliedFormat":1},{"version":"29972445d3c4c90f33eff093c52daabff847185f23e006b906ae582db47c1b76","impliedFormat":1},{"version":"c64d10c169ca1c7e662f229f619455d3710a9be5caea81ac890939dcef6eb639","impliedFormat":1},{"version":"311d23b5021473e0dec2266bd53aed658b96e1610f2bf5b00d4ede63f52458e4","impliedFormat":1},{"version":"403303182c36648089590004be2e9d41327dbbf49d2bcb49300e079abf30a48a","impliedFormat":1},{"version":"47f3f9181c8899d12ec3085fd59d17f8fd34d827771a3d3ffb08e217e3f4045e","impliedFormat":1},{"version":"064252d997cd14b1baa1ba7df575a1fa127aec5c50c6795ffb420377931f8927","impliedFormat":1},{"version":"314f629df4188e92ce501c35ad66513232d8ea54a94c9c55052c6cc6a57cb404","impliedFormat":1},{"version":"619a9313defd6fcf0951b7700bafd0270db7c54266ac279848bb71a182a91b39","impliedFormat":1},{"version":"9c3c3bda90321687067b0fa23d0348ca5091faef71281d375a8c6b1dd646602d","impliedFormat":1},{"version":"a1289a4bc5fc40c5ee4c0bb1123b686aeba2c617021a47dc6f0f936b18287428","impliedFormat":1},{"version":"175c039f6d6d6b441d6929e35a52d983fc31aae1d90cc082499472be847bff44","impliedFormat":1},{"version":"4906f21f2637d60c009ba11fadcb55f3461d32fd4053bdc470837e2d492a4949","impliedFormat":1},{"version":"f33b9fd731f271ed3e635214adb7f13c8061a733d9e0e4f3f7253e54c633e1d5","impliedFormat":1},{"version":"0c609838f7b5a10b03fef513bbfd7d056bf0e6fb0cfae6baa0be1d85341dc295","impliedFormat":1},{"version":"f7988a5d76deb4c742941e891dd2a6f7640b8c67ef4c10dd780be80d04282510","impliedFormat":1},{"version":"92567a54f7e9d10ae38666c6182d757309e94e36b763044b2aefbe6b31e443ea","impliedFormat":1},{"version":"f41fa86abb13df8c06e3dd545175772fd6ac7f40639f2437fbcaeeef9251536d","impliedFormat":1},{"version":"eb1f0079ad411d3d282a29c721c89ab4878d514eb361ac7c05041147c8b99a89","impliedFormat":1},{"version":"790551602f511013dbd06590159e6aebd44a15127a7ddec7eaeeddd6af7eb239","impliedFormat":1},{"version":"36b408cad01f9cb2739b34a99b333f2ee8e9b27c4363176ea9b4aafd35b692b0","impliedFormat":1},{"version":"841aa7ecb51c9d4046f6c33c640b0366849e21fa0de6498a08158a1c5dd1506e","impliedFormat":1},{"version":"b8513a4e4d3f02612d111adbbde8ae00a6adfdb33892fb72e84662144899b562","impliedFormat":1},{"version":"b87d1a554ec977c7a7797601313412b6769544f638104787b0136d4fd6fb12f3","impliedFormat":1},{"version":"841aa7ecb51c9d4046f6c33c640b0366849e21fa0de6498a08158a1c5dd1506e","impliedFormat":1},{"version":"147abcf1d48a19553366fec6b72191ecf6f83d8e4c1260436c4f854883bba65c","impliedFormat":1},{"version":"61e8f63ea286d27ce20bc07baed9a30b0e18b0a3ae66b6a9e6aa7353e37c5111","impliedFormat":1},{"version":"5c8a7c35b3ededbbf0b7ddbd5e99a50061d424946da2ed5abbbbbdb3989effd0","impliedFormat":1},{"version":"ee020464da44a0b6a825c9a0d3a2ecab3e4c028eb005b4fce44c53b4959114fd","impliedFormat":1},{"version":"7e88d2eef3de0e141de5c7b3140c5aade44be358a5a6ed30a7595b625868cf77","impliedFormat":1},{"version":"f738149a907a25b221347bac1f18d712e84fb5f4ef9cabdd80f2f8e7a913e35c","impliedFormat":1},{"version":"9e3ac0be1bae2b985b57fd0d4c87b8eb4b39fe0d498fe5b1d6d72402e5a0c523","impliedFormat":1},{"version":"fdbcc3f4c8b0aec2a53cd2949ed1366aa890c48285d216a7b2e4df76ca1702a7","impliedFormat":1},{"version":"c746651f3145fc12d1efa456fe7eb530102acdb242a2e5720ab03bf790fab81e","impliedFormat":1},{"version":"bfea1dc7fa119165ec11be5efceb5305638bfe20cb9ab26f770b9bbaac393106","impliedFormat":1},{"version":"5de2a1985422dfc9f8ad07d8d96abcbf4b2d346d6ffa4c2d77ff730c4c11e746","signature":"2bca72be9ea45128710d0962aca2ad39d071d0b0ecf688e7495fabaf097eac24"},{"version":"c8f9010115a98656a3a96e67bbb2f9ad523f125dfad4ab92eec067129f00c2e5","signature":"75fcc9b1b71cd74713931f7357580f05d4481d045aef310b8d7acb7c24afba1e"},{"version":"ff44aa955f35492b5c4602acbfcd613498d25ac276004ddb5191bd23e6abced3","signature":"e46c7f748216edbbf7ef775fb6a9c69eb182176a01e74a980be210fe3e8a3a33"},{"version":"180d3e6c7a84c35728761fcac55f64f1505be1ee33da0df10a092d93146b9e62","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"d153a11543fd884b596587ccd97aebbeed950b26933ee000f94009f1ab142848","affectsGlobalScope":true,"impliedFormat":1},{"version":"0ccdaa19852d25ecd84eec365c3bfa16e7859cadecf6e9ca6d0dbbbee439743f","affectsGlobalScope":true,"impliedFormat":1},{"version":"cc2110f7decca6bfb9392e30421cfa1436479e4a6756e8fec6cbc22625d4f881","affectsGlobalScope":true,"impliedFormat":1},{"version":"096116f8fedc1765d5bd6ef360c257b4a9048e5415054b3bf3c41b07f8951b0b","affectsGlobalScope":true,"impliedFormat":1},{"version":"e5e01375c9e124a83b52ee4b3244ed1a4d214a6cfb54ac73e164a823a4a7860a","affectsGlobalScope":true,"impliedFormat":1},{"version":"f90ae2bbce1505e67f2f6502392e318f5714bae82d2d969185c4a6cecc8af2fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"4b58e207b93a8f1c88bbf2a95ddc686ac83962b13830fe8ad3f404ffc7051fb4","affectsGlobalScope":true,"impliedFormat":1},{"version":"1fefabcb2b06736a66d2904074d56268753654805e829989a46a0161cd8412c5","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"c18a99f01eb788d849ad032b31cafd49de0b19e083fe775370834c5675d7df8e","affectsGlobalScope":true,"impliedFormat":1},{"version":"5247874c2a23b9a62d178ae84f2db6a1d54e6c9a2e7e057e178cc5eea13757fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"cdcf9ea426ad970f96ac930cd176d5c69c6c24eebd9fc580e1572d6c6a88f62c","impliedFormat":1},{"version":"23cd712e2ce083d68afe69224587438e5914b457b8acf87073c22494d706a3d0","impliedFormat":1},{"version":"156a859e21ef3244d13afeeba4e49760a6afa035c149dda52f0c45ea8903b338","impliedFormat":1},{"version":"10ec5e82144dfac6f04fa5d1d6c11763b3e4dbbac6d99101427219ab3e2ae887","impliedFormat":1},{"version":"615754924717c0b1e293e083b83503c0a872717ad5aa60ed7f1a699eb1b4ea5c","impliedFormat":1},{"version":"14e9acf826baba0ef4b5665704084896e7bcc06f65a9ab13af7e93d27d6b7069","impliedFormat":1},{"version":"68834d631c8838c715f225509cfc3927913b9cc7a4870460b5b60c8dbdb99baf","impliedFormat":1},{"version":"21adf13435b9b748529c8cedf80f884e5130b9684188120a686cd2b26a2059c7","impliedFormat":1},{"version":"eec76bf6b9346f3f95fa402621b889489e96930e72295b0369022f332e9b4a6a","impliedFormat":1},{"version":"0ecd58f413f9bc3b7d4383eae31b0c8fc576985cd7404d6f99f8c643543ade74","impliedFormat":1},{"version":"ea6bc8de8b59f90a7a3960005fd01988f98fd0784e14bc6922dde2e93305ec7d","impliedFormat":1},{"version":"36107995674b29284a115e21a0618c4c2751b32a8766dd4cb3ba740308b16d59","impliedFormat":1},{"version":"914a0ae30d96d71915fc519ccb4efbf2b62c0ddfb3a3fc6129151076bc01dc60","impliedFormat":1},{"version":"9c32412007b5662fd34a8eb04292fb5314ec370d7016d1c2fb8aa193c807fe22","impliedFormat":1},{"version":"7fd1b31fd35876b0aa650811c25ec2c97a3c6387e5473eb18004bed86cdd76b6","impliedFormat":1},{"version":"4d327f7d72ad0918275cea3eee49a6a8dc8114ae1d5b7f3f5d0774de75f7439a","impliedFormat":1},{"version":"6ebe8ebb8659aaa9d1acbf3710d7dae3e923e97610238b9511c25dc39023a166","impliedFormat":1},{"version":"e85d7f8068f6a26710bff0cc8c0fc5e47f71089c3780fbede05857331d2ddec9","impliedFormat":1},{"version":"7befaf0e76b5671be1d47b77fcc65f2b0aad91cc26529df1904f4a7c46d216e9","impliedFormat":1},{"version":"0a60a292b89ca7218b8616f78e5bbd1c96b87e048849469cccb4355e98af959a","impliedFormat":1},{"version":"0b6e25234b4eec6ed96ab138d96eb70b135690d7dd01f3dd8a8ab291c35a683a","impliedFormat":1},{"version":"9666f2f84b985b62400d2e5ab0adae9ff44de9b2a34803c2c5bd3c8325b17dc0","impliedFormat":1},{"version":"40cd35c95e9cf22cfa5bd84e96408b6fcbca55295f4ff822390abb11afbc3dca","impliedFormat":1},{"version":"b1616b8959bf557feb16369c6124a97a0e74ed6f49d1df73bb4b9ddf68acf3f3","impliedFormat":1},{"version":"5b03a034c72146b61573aab280f295b015b9168470f2df05f6080a2122f9b4df","impliedFormat":1},{"version":"40b463c6766ca1b689bfcc46d26b5e295954f32ad43e37ee6953c0a677e4ae2b","impliedFormat":1},{"version":"249b9cab7f5d628b71308c7d9bb0a808b50b091e640ba3ed6e2d0516f4a8d91d","impliedFormat":1},{"version":"d33ce35e3f9cfcc1d94eca415bdd3bde94d5b153ffdd33e6c4455c029986c630","impliedFormat":1},{"version":"80aae6afc67faa5ac0b32b5b8bc8cc9f7fa299cff15cf09cc2e11fd28c6ae29e","impliedFormat":1},{"version":"f473cd2288991ff3221165dcf73cd5d24da30391f87e85b3dd4d0450c787a391","impliedFormat":1},{"version":"499e5b055a5aba1e1998f7311a6c441a369831c70905cc565ceac93c28083d53","impliedFormat":1},{"version":"8aee8b6d4f9f62cf3776cda1305fb18763e2aade7e13cea5bbe699112df85214","impliedFormat":1},{"version":"98498b101803bb3dde9f76a56e65c14b75db1cc8bec5f4db72be541570f74fc5","impliedFormat":1},{"version":"4dc59f6e1dbf3d5f66660fceabe6c174d3261b37b696ae1854f0dbaf255fc753","impliedFormat":1},{"version":"5d0375ca7310efb77e3ef18d068d53784faf62705e0ad04569597ae0e755c401","impliedFormat":1},{"version":"59af37caec41ecf7b2e76059c9672a49e682c1a2aa6f9d7dc78878f53aa284d6","impliedFormat":1},{"version":"addf417b9eb3f938fddf8d81e96393a165e4be0d4a8b6402292f9c634b1cb00d","impliedFormat":1},{"version":"436d7b4543b340b0f3eef4310d524242e41369b9652aa9c70428767c4dcac455","impliedFormat":1},{"version":"adf27937dba6af9f08a68c5b1d3fce0ca7d4b960c57e6d6c844e7d1a8e53adae","impliedFormat":1},{"version":"12950411eeab8563b349cb7959543d92d8d02c289ed893d78499a19becb5a8cc","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"114f493b30f364255290472111b5a4791d5902c308645670cd0401429cbc6930","impliedFormat":1},{"version":"c3f5289820990ab66b70c7fb5b63cb674001009ff84b13de40619619a9c8175f","affectsGlobalScope":true,"impliedFormat":1},{"version":"b3275d55fac10b799c9546804126239baf020d220136163f763b55a74e50e750","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa68a0a3b7cb32c00e39ee3cd31f8f15b80cac97dce51b6ee7fc14a1e8deb30b","affectsGlobalScope":true,"impliedFormat":1},{"version":"1cf059eaf468efcc649f8cf6075d3cb98e9a35a0fe9c44419ec3d2f5428d7123","affectsGlobalScope":true,"impliedFormat":1},{"version":"6c36e755bced82df7fb6ce8169265d0a7bb046ab4e2cb6d0da0cb72b22033e89","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"7a93de4ff8a63bafe62ba86b89af1df0ccb5e40bb85b0c67d6bbcfdcf96bf3d4","affectsGlobalScope":true,"impliedFormat":1},{"version":"90e85f9bc549dfe2b5749b45fe734144e96cd5d04b38eae244028794e142a77e","affectsGlobalScope":true,"impliedFormat":1},{"version":"e0a5deeb610b2a50a6350bd23df6490036a1773a8a71d70f2f9549ab009e67ee","affectsGlobalScope":true,"impliedFormat":1},{"version":"d2ae155afe8a01cc0ae612d99117cf8ef16692ba7c4366590156fdec1bcf2d8c","impliedFormat":1},{"version":"3f5e5d9be35913db9fea42a63f3df0b7e3c8703b97670a2125587b4dbbd56d7c","impliedFormat":1},{"version":"c8b8968311ec4e5e97b7b5fb8a65efaba455db9bdcfd7fff7fb15f6e317bfba0","impliedFormat":1},{"version":"57c23df0b5f7a8e26363a3849b0bc7763f6b241207157c8e40089d1df4116f35","affectsGlobalScope":true,"impliedFormat":1},{"version":"3b8bc0c17b54081b0878673989216229e575d67a10874e84566a21025a2461ee","impliedFormat":1},{"version":"5b0db5a58b73498792a29bfebc333438e61906fef75da898b410e24e52229e6f","impliedFormat":1},{"version":"dbe055b2b29a7bab2c1ca8f259436306adb43f469dca7e639a02cd3695d3f621","impliedFormat":1},{"version":"1678b04557dca52feab73cc67610918a7f5e25bfdba3e7fa081acd625d93106d","impliedFormat":1},{"version":"aecbf1d9e6a18dab7d92ef8a89a1444b47e1eb6134cb2bb776a26d55ff58c29a","impliedFormat":1},{"version":"2ea729503db9793f2691162fec3dd1118cab62e96d025f8eeb376d43ec293395","impliedFormat":1},{"version":"9ec87fea42b92894b0f209931a880789d43c3397d09dd99c631ae40a2f7071d1","impliedFormat":1},{"version":"c68e88cdfadfb6c8ba5fc38e58a3a166b0beae77b1f05b7d921150a32a5ffb8d","impliedFormat":1},{"version":"2bc7aa4fba46df0bd495425a7c8201437a7d465f83854fac859df2d67f664df3","impliedFormat":1},{"version":"41d17e1ad9a002feb11c8cdd2777e5bbc0cdb1e3f595d237e4dded0b6949983b","impliedFormat":1},{"version":"1fede9296beac11ce8e6b425396a1791f64341f2be85deebb6286faf6e16306e","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce697b6a251d9cad53998c7fd3098072df883b525ec45d83530e434dc6d80dc6","impliedFormat":1},{"version":"719412f054e6ecc35489462c9a21bab0323d173a7d04e55b0ace4b5d86fbeb07","impliedFormat":1},{"version":"0eb5d0cbf09de5d34542b977fd6a933bb2e0817bffe8e1a541b2f1ad1b9af1ff","impliedFormat":1},{"version":"fac3e88881b35d3a757ed891ac912b2674792c25e2a1a74e1f5fbc72d19a9792","impliedFormat":1},{"version":"2c2bdaa1d8ead9f68628d6d9d250e46ee8e81aa4898b4769a36956ae15e060fe","impliedFormat":1},{"version":"c32c840c62d8bd7aeb3147aa6754cd2d922b990a6b6634530cb2ebdce5adc8e9","impliedFormat":1},{"version":"5ff4433a2deae4f85ab1377e90a7554ce6b47ae51c69a84ca30a6e22fae85834","impliedFormat":1},{"version":"82b91e4e42e6c41bc7fc1b6c2dc5eba6a2ba98375eb1f210e6ff6bba2d54177e","impliedFormat":1},{"version":"c1fa52b3d014001e8662fa2669d90ea15373958a288e3b83a3b621733d25292a","affectsGlobalScope":true,"impliedFormat":1},{"version":"cbed824fec91efefc7bbdcb8b43d1a531fdbebd0e2ef19481501ff365a93cb70","impliedFormat":1},{"version":"d0716593b3f2b0451bcf0c24cfa86dec2235c325c89f201934248b7c742715fc","impliedFormat":1},{"version":"ec501101c2a96133a6c695f934c8f6642149cc728571b29cbb7b770984c1088e","impliedFormat":1},{"version":"b214ebcf76c51b115453f69729ee8aa7b7f8eccdae2a922b568a45c2d7ff52f7","impliedFormat":1},{"version":"429c9cdfa7d126255779efd7e6d9057ced2d69c81859bbab32073bad52e9ba76","impliedFormat":1},{"version":"2991bca2cc0f0628a278df2a2ccdb8d6cbcb700f3761abbed62bba137d5b1790","impliedFormat":1},{"version":"5e66972e83eb4dc7123939bf816e6cbd9ad81af5552db1cab84e6bd9c64d2ecc","affectsGlobalScope":true,"impliedFormat":1},{"version":"230763250f20449fa7b3c9273e1967adb0023dc890d4be1553faca658ee65971","impliedFormat":1},{"version":"c3e9078b60cb329d1221f5878e88cecfa3e74460550e605a58fcfb41a66029ff","impliedFormat":1},{"version":"8413d0641f293aed551c7464615b770d34a02dedede889b9591172287d68e773","impliedFormat":1},{"version":"0ea59f7d3e51440baa64f429253759b106cfcbaf51e474cae606e02265b37cf8","impliedFormat":1},{"version":"bc18a1991ba681f03e13285fa1d7b99b03b67ee671b7bc936254467177543890","impliedFormat":1},{"version":"1b241e24f3227d078c06aeda6e050187ad59a4e591f4467abed44d92b084e08d","impliedFormat":1},{"version":"fa94bbf532b7af8f394b95fa310980d6e20bd2d4c871c6a6cb9f70f03750a44b","impliedFormat":1},{"version":"7fde0e1be5c8be204ffbf428abfcf01da2eb0f130e1bc3f539eb7275f4fd1f58","impliedFormat":1},{"version":"e284328553df5f425a5d33d36a0c3fa66b46af9d097cad6f4d2e8696dfdeb0f1","affectsGlobalScope":true,"impliedFormat":1},{"version":"7fa2214bb0d64701bc6f9ce8cde2fd2ff8c571e0b23065fa04a8a5a6beb91511","impliedFormat":1},{"version":"f36b3fbe2be150a9ca140da48593f21e6a8172004f92ddc549b43efec39f3e54","impliedFormat":1},{"version":"f1c93e046fb3d9b7f8249629f4b63dc068dd839b824dd0aa39a5e68476dc9420","impliedFormat":1},{"version":"016b29bf4926b80255a108c53a1451717350059da04fcae64d1075f5e93bbb39","impliedFormat":1},{"version":"841983e39bd4cbb463be385e92fda11057cab368bf27100a801c492f1d86cbaa","impliedFormat":1},{"version":"1c4f139ade4f6ebf45463505f8155173e5d7a5305e50e0aae0a5e712d6ff3b48","impliedFormat":1},{"version":"e16b319e5aca1031168de823c4946ff8e29629c4c8cc0ec0fcfe2a8ab2155043","impliedFormat":1},{"version":"e4156ddb25aa0e3b5303d372f26957b36778f0f6bbd4326359269873295e3058","affectsGlobalScope":true,"impliedFormat":1},{"version":"cc1b433a84cae05ddc5672d4823170af78606ad21ecef60dbc4570190cbf1357","impliedFormat":1},{"version":"9d3821bc75c59577e52643324cec92fc2145642e8d17cf7ee07a3181f21d985d","impliedFormat":1},{"version":"7f78cfb2b343838612c192cb251746e3a7c62ac7675726a47e130d9b213f6580","impliedFormat":1},{"version":"201db9cf1687fab1adf5282fcba861f382b32303dc4f67c89d59655e78a25461","impliedFormat":1},{"version":"2c3c5c0f54055e87640f5d233716fd889f3034fc7911d603b642369b0dbeb2a7","impliedFormat":1},{"version":"0a20eaf2e4b1e3c1e1f87f7bccb0c936375b23b022baeea750519b7c9bc6ce83","impliedFormat":1},{"version":"b484ec11ba00e3a2235562a41898d55372ccabe607986c6fa4f4aba72093749f","impliedFormat":1},{"version":"a16b91b27bd6b706c687c88cbc8a7d4ee98e5ed6043026d6b84bda923c0aed67","impliedFormat":1},{"version":"1c9e5b1a17b1fc9b3711fb36e0690421261ab2880f15b145155b5b2ba2ab6c2d","impliedFormat":1},{"version":"99ab6d0d660ce4d21efb52288a39fd35bb3f556980ec5463b1ae8f304a3bbc85","impliedFormat":1},{"version":"6eeded8c7e352be6e0efb83f4935ec752513c4d22043b52522b90849a49a3a11","impliedFormat":1},{"version":"6c1ad90050ffbb151cacc68e2d06ea1a26a945659391e32651f5d42b86fd7f2c","impliedFormat":1},{"version":"afa1c49f8e559e413d57343339db857d2a8159435cf9cf7d4deb41718fff1b88","impliedFormat":1},{"version":"6953d7597831d0860c7034cf4f0419687d263b6b98a4b32e37ce6d49615c36e2","impliedFormat":1}],"root":[60,[64,67],[70,76],[277,279],[325,328]],"options":{"composite":true,"declaration":true,"declarationMap":true,"esModuleInterop":true,"module":7,"outDir":"./dist","rootDir":"./src","skipLibCheck":true,"strict":true,"target":9},"referencedMap":[[298,1],[299,1],[301,2],[308,3],[300,1],[307,4],[303,1],[304,5],[306,6],[302,1],[305,1],[287,7],[281,7],[324,8],[323,9],[322,10],[286,11],[289,12],[283,13],[282,14],[297,15],[321,16],[288,17],[320,18],[290,19],[295,20],[292,21],[293,22],[291,23],[296,24],[294,25],[285,26],[314,27],[313,27],[311,27],[315,28],[312,27],[317,27],[319,29],[310,7],[318,7],[284,30],[309,27],[316,7],[62,31],[63,32],[61,27],[91,27],[147,33],[148,34],[149,35],[150,35],[146,35],[151,34],[152,34],[158,36],[153,33],[154,34],[145,27],[155,34],[156,34],[157,33],[175,37],[176,27],[177,38],[178,37],[179,38],[180,38],[182,39],[183,27],[184,40],[185,38],[186,38],[194,41],[187,42],[188,37],[189,42],[190,43],[191,43],[192,43],[193,38],[181,27],[159,27],[160,38],[161,44],[162,44],[163,44],[164,44],[165,44],[166,44],[167,38],[174,45],[168,38],[169,44],[170,44],[171,44],[172,44],[173,38],[195,27],[196,38],[197,38],[198,38],[199,38],[201,38],[200,38],[204,46],[202,27],[203,38],[210,47],[209,48],[206,49],[205,27],[208,50],[207,50],[143,27],[214,51],[144,7],[213,52],[211,53],[212,53],[217,27],[218,54],[221,55],[219,56],[220,53],[77,7],[78,7],[79,7],[80,7],[81,7],[82,7],[83,7],[84,7],[85,7],[86,7],[87,7],[88,7],[89,7],[90,7],[95,57],[108,58],[96,7],[97,7],[98,7],[99,7],[102,59],[103,7],[104,7],[105,7],[106,7],[107,7],[109,7],[110,27],[111,27],[112,7],[115,60],[113,61],[114,62],[116,57],[119,63],[117,64],[118,65],[93,66],[122,67],[120,61],[121,68],[125,69],[123,61],[124,68],[126,62],[242,70],[127,7],[130,71],[128,61],[129,68],[131,7],[134,72],[132,61],[133,62],[137,73],[135,61],[136,68],[225,68],[226,65],[238,7],[241,74],[239,61],[240,75],[231,68],[138,7],[141,76],[139,61],[140,68],[142,7],[216,77],[215,78],[222,79],[224,80],[223,79],[227,7],[230,81],[228,61],[229,75],[232,7],[237,82],[233,61],[236,7],[234,7],[235,75],[276,83],[244,61],[245,61],[246,61],[243,7],[247,61],[248,61],[249,61],[270,61],[265,84],[250,61],[273,85],[251,61],[252,61],[253,61],[267,61],[254,61],[255,61],[268,61],[256,61],[266,27],[271,61],[272,61],[257,61],[258,61],[269,86],[259,61],[101,61],[260,61],[261,61],[262,61],[263,61],[100,27],[264,87],[94,88],[275,89],[92,88],[274,90],[69,91],[392,92],[393,92],[394,93],[331,94],[395,95],[396,96],[397,97],[329,27],[398,98],[399,99],[400,100],[401,101],[402,102],[403,103],[404,103],[405,104],[406,105],[407,106],[408,107],[332,27],[330,27],[409,108],[410,109],[411,110],[453,111],[412,112],[413,113],[414,112],[415,114],[416,115],[417,116],[418,117],[419,117],[420,117],[421,118],[422,119],[423,120],[424,121],[425,122],[426,123],[427,123],[428,124],[429,27],[430,27],[431,125],[432,126],[433,127],[434,125],[435,128],[436,129],[437,130],[438,131],[439,132],[440,133],[441,134],[442,135],[443,136],[444,137],[445,138],[446,139],[447,140],[448,141],[449,142],[333,112],[334,27],[335,143],[336,144],[337,27],[338,145],[339,27],[383,146],[384,147],[385,148],[386,148],[387,149],[388,27],[389,95],[390,150],[391,147],[450,151],[451,152],[452,153],[68,27],[280,27],[58,27],[59,27],[11,27],[10,27],[2,27],[12,27],[13,27],[14,27],[15,27],[16,27],[17,27],[18,27],[19,27],[3,27],[20,27],[21,27],[4,27],[22,27],[26,27],[23,27],[24,27],[25,27],[27,27],[28,27],[29,27],[5,27],[30,27],[31,27],[32,27],[33,27],[6,27],[37,27],[34,27],[35,27],[36,27],[38,27],[7,27],[39,27],[44,27],[45,27],[40,27],[41,27],[42,27],[43,27],[8,27],[49,27],[46,27],[47,27],[48,27],[50,27],[9,27],[51,27],[52,27],[53,27],[55,27],[54,27],[56,27],[1,27],[57,27],[358,154],[371,155],[355,156],[372,157],[381,158],[346,159],[347,160],[345,161],[380,162],[375,163],[379,164],[349,165],[368,166],[348,167],[378,168],[343,169],[344,163],[350,170],[351,27],[357,171],[354,170],[341,172],[382,173],[373,174],[361,175],[360,170],[362,176],[365,177],[359,178],[363,179],[376,162],[352,180],[353,181],[366,182],[342,157],[370,183],[369,170],[356,181],[364,184],[367,185],[374,27],[340,27],[377,186],[279,187],[326,188],[60,27],[64,189],[327,190],[325,27],[66,191],[75,192],[74,193],[76,194],[70,27],[67,27],[71,195],[328,196],[72,27],[65,144],[277,197],[278,198],[73,27]],"latestChangedDtsFile":"./dist/server.d.ts","version":"6.0.3"} \ No newline at end of file