diff --git a/CHANGELOG.md b/CHANGELOG.md index 80d5135..aee8481 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,27 @@ All notable changes to `@fundable/sdk` are documented here. +## 0.2.3 + +Hardened Soroban contract alignment release. + +### Added + +- Administrative and maintenance clients on `client.admin` (`StellarAdminClient`, `FlowAdminClient`, `LockupAdminClient`, `RouterAdminClient`) exposing: + - `proposeUpgrade`, `executeUpgrade`, and direct `upgrade` + - Two-step admin transfers: `proposeAdmin`, `acceptAdmin` + - Stream TTL extension: `extendStreamTtl` + - Router NFT upgrades: `upgradeNft` +- Comprehensive Soroban error mappings covering Flow 1–28 (e.g. `TokenTransferMismatch`, `TokenDecimalsMismatch`, `ArithmeticError`), Lockup 101–120 (including corrected 111 `NegativeUnlockAmount`, `StartUnlockAmountExceedsTotal`, `GranularityZero`), Stream NFT 201–205, Router 301–305 (`InvalidContractAddress`), Paymaster 401–408. +- Client-side validations for Lockup unlock amounts (`assertLockupUnlockAmounts`) and token decimals (`assertTokenDecimalsMatch`). +- Stream NFT delegated queries for Router stream metadata and status. + +### Changed + +- Regenerated TypeScript contract bindings for Flow, Lockup, Router, Stream NFT, and Paymaster from hardened contract WASM builds. +- Router `createFlow` and `createLockup` aligned with hardened contract interface, delegating stream token minting and indexing to the Stream NFT contract. +- Re-exported error parsing and Soroban error definitions (`SOROBAN_CONTRACT_ERRORS`, `parseSorobanErrorCode`, `translateSorobanError`, `toStellarFundableError`). + ## 0.2.1 ### Fixed diff --git a/docs/generated-bindings.md b/docs/generated-bindings.md index 9d5cd85..e62c78c 100644 --- a/docs/generated-bindings.md +++ b/docs/generated-bindings.md @@ -88,3 +88,34 @@ Reproducible WASM SHA-256 values: The SDK `0.2.0` bindings were generated from the four reproducible release artifacts with the corresponding `pnpm generate:* -- /absolute/path.wasm` commands. The transitional Paymaster was intentionally not regenerated. + +## Hardened contract release provenance (SDK v0.2.3) + +- Contract verification: `make build` and `cargo test` (98 tests passed) +- Stellar CLI: `stellar 22.0.2` + +Optimized WASM SHA-256 values: + +| Contract | SHA-256 | +| --- | --- | +| Flow | `3903d1dee59ef048a64d5287bb9e27e05d54c3c8dc3962e28c16b2031d074906` | +| Lockup | `e752e3589144c340136c0fa6a1ab344dd794ad547d3fe0bebcce7c1f995b59fc` | +| Router | `36e881b771aa6f64bb0d96915e61e6d65a0f976bce332b4b73102ac8d228c4c9` | +| Stream NFT | `61ec4bfa817780d65ed439b01dc39d703162c6801346427df7a05d4256f94726` | +| Paymaster | `b068fcfa6103a2686997939d1942629c9f97ce941df85109fd0181f4622e9195` | + +The bindings were regenerated with `scripts/generate-stellar-binding.mjs` for all five contracts: +- `pnpm generate:flow -- /path/to/flow.wasm` +- `pnpm generate:lockup -- /path/to/lockup.wasm` +- `pnpm generate:router -- /path/to/router.wasm` +- `pnpm generate:stream-nft -- /path/to/stream_nft.wasm` +- `pnpm generate:paymaster -- /path/to/paymaster.wasm` + +Key contract changes reflected in SDK v0.2.3: +- Contract initialization (`initialize`) methods require explicit `admin` authorization. +- Two-step administrative transfers: `propose_admin` followed by `accept_admin`. +- Timelocked contract upgrades: `propose_upgrade` followed by `execute_upgrade` (or direct `upgrade` for immediate upgrades). +- Stream TTL extension: `extend_stream_ttl`. +- Router delegates NFT ownership and stream indexing to the Stream NFT contract; `create_flow_stream` and `create_lockup_stream` cleanly handle routing without redundant caller parameters. +- Comprehensive contract error coverage mapped: Flow 1–28, Lockup 101–120, Stream NFT 201–205, Router 301–305, Paymaster 401–408. + diff --git a/package.json b/package.json index f34f28c..4d79bc9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fundable/sdk", - "version": "0.2.1", + "version": "0.2.3", "private": false, "description": "Multichain TypeScript SDK for Fundable Protocol; Stellar adapter included", "license": "MIT", diff --git a/src/generated/flow/src/index.ts b/src/generated/flow/src/index.ts index 5cbefc7..ee8539b 100644 --- a/src/generated/flow/src/index.ts +++ b/src/generated/flow/src/index.ts @@ -27,7 +27,7 @@ export * as rpc from "@stellar/stellar-sdk/rpc"; /** * Core data structure for a Flow (open-ended, rate-per-second) stream. - * + * * Ported from Sablier's `Flow.Stream` struct with these adaptations: * * - `UD21x18 ratePerSecond` → `i128 rate_per_second` scaled to 18 decimals. @@ -219,37 +219,6 @@ export enum StreamStatus { } -/** - * Stable public metadata for a Stream NFT and its underlying core stream. - */ -export interface StreamMetadata { - /** - * Internal identifier assigned by the selected core engine. - */ -core_stream_id: u64; - /** - * Current holder of withdrawal and NFT-owner Flow void rights. - */ -owner: string; - /** - * Canonical cross-engine lifecycle state. - */ -status: CanonicalStreamStatus; - /** - * Core engine kind. - */ -stream_type: StreamType; - /** - * Public, deployment-scoped stream identifier. - */ -token_id: i128; - /** - * Immutable transferability policy selected when the stream is created. - */ -transferable: boolean; -} - - /** * Parameters for creating a new Lockup stream. * @@ -303,21 +272,6 @@ token: string; total_amount: i128; } -/** - * Canonical lifecycle exposed by the Router to every product surface. - * - * Core engines retain their more detailed accounting states; the Router maps - * those states into this stable cross-engine vocabulary. - */ -export enum CanonicalStreamStatus { - Pending = 0, - Active = 1, - Paused = 2, - Canceled = 3, - Completed = 4, - Failed = 5, -} - /** * Errors specific to the Stream NFT contract. */ @@ -421,9 +375,33 @@ export const FlowError = { */ 21: {message:"NegativeRate"}, /** - * The token did not debit and credit the exact requested amount. + * Token transfer resulted in a different balance change than requested. + */ + 22: {message:"TokenTransferMismatch"}, + /** + * Caller-supplied token decimals do not match the token contract. + */ + 23: {message:"TokenDecimalsMismatch"}, + /** + * Checked arithmetic operation failed (overflow/underflow). + */ + 24: {message:"ArithmeticError"}, + /** + * Admin transfer already pending. */ - 22: {message:"TokenTransferMismatch"} + 25: {message:"AdminTransferPending"}, + /** + * No admin transfer pending to accept. + */ + 26: {message:"NoAdminTransferPending"}, + /** + * Upgrade is timelocked and cannot be executed yet. + */ + 27: {message:"UpgradeTimelocked"}, + /** + * No upgrade has been proposed. + */ + 28: {message:"NoUpgradeProposed"} } /** @@ -471,13 +449,45 @@ export const LockupError = { */ 110: {message:"SenderEqualsRecipient"}, /** - * Start and cliff unlock amounts must be nonnegative and fit in total amount. + * start_unlock_amount or cliff_unlock_amount is negative. + */ + 111: {message:"NegativeUnlockAmount"}, + /** + * Checked addition of unlock amounts overflowed. + */ + 112: {message:"UnlockSumOverflow"}, + /** + * Unlock sum is not in the valid range [0, total_amount]. */ - 111: {message:"InvalidUnlockAmount"}, + 113: {message:"InvalidUnlockSum"}, /** - * The token did not debit and credit the exact requested amount. + * Defensive cancellation amount validation failed. */ - 112: {message:"TokenTransferMismatch"} + 114: {message:"InvalidCancellationAmount"}, + /** + * Token transfer resulted in a different balance change than requested. + */ + 115: {message:"TokenTransferMismatch"}, + /** + * Checked arithmetic operation failed (overflow/underflow). + */ + 116: {message:"ArithmeticError"}, + /** + * Admin transfer already pending. + */ + 117: {message:"AdminTransferPending"}, + /** + * No admin transfer pending to accept. + */ + 118: {message:"NoAdminTransferPending"}, + /** + * Upgrade is timelocked and cannot be executed yet. + */ + 119: {message:"UpgradeTimelocked"}, + /** + * No upgrade has been proposed. + */ + 120: {message:"NoUpgradeProposed"} } /** @@ -489,9 +499,9 @@ export const RouterError = { 303: {message:"NotAuthorized"}, 304: {message:"InvalidStreamType"}, /** - * Core contract addresses were already configured. + * A required contract address is invalid (zero/unset). */ - 305: {message:"AlreadyConfigured"} + 305: {message:"InvalidContractAddress"} } /** @@ -500,7 +510,7 @@ export const RouterError = { * Using a typed enum prevents key collisions (SKILL.md §3). * Keys are namespaced by variant to keep different data types separate. */ -export type DataKey = {tag: "Admin", values: void} | {tag: "NextStreamId", values: void} | {tag: "FlowStream", values: readonly [u64]} | {tag: "LockupStream", values: readonly [u64]} | {tag: "AggregateBalance", values: readonly [string]} | {tag: "TokenOwner", values: readonly [i128]} | {tag: "TokenStreamData", values: readonly [i128]} | {tag: "TokenTransferable", values: readonly [i128]} | {tag: "NftBalance", values: readonly [string]} | {tag: "TokenMetadata", values: readonly [string]} | {tag: "FlowContract", values: void} | {tag: "LockupContract", values: void} | {tag: "NftContract", values: void} | {tag: "Router", values: void} | {tag: "AllowedFeeTokens", values: void}; +export type DataKey = {tag: "Admin", values: void} | {tag: "NextStreamId", values: void} | {tag: "FlowStream", values: readonly [u64]} | {tag: "LockupStream", values: readonly [u64]} | {tag: "AggregateBalance", values: readonly [string]} | {tag: "TokenOwner", values: readonly [i128]} | {tag: "TokenStreamData", values: readonly [i128]} | {tag: "NftBalance", values: readonly [string]} | {tag: "TokenMetadata", values: readonly [string]} | {tag: "FlowContract", values: void} | {tag: "LockupContract", values: void} | {tag: "NftContract", values: void} | {tag: "AllowedFeeTokens", values: void} | {tag: "PendingAdmin", values: void} | {tag: "ProposedUpgrade", values: readonly [Buffer]} | {tag: "UpgradeUnlockLedger", values: void}; export interface Client { /** @@ -558,9 +568,11 @@ export interface Client { /** * Construct and simulate a upgrade transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Upgrade the contract WASM bytecode. + * Emergency upgrade — bypasses timelock. Use only in critical situations. + * Emits an upgrade event for auditing. * - * Admin-only. Per SKILL.md §7: admin-gated upgrades with event emission. + * Admin-only. Consider removing this function after initial mainnet + * stabilization, or requiring a separate emergency key. */ upgrade: ({new_wasm_hash}: {new_wasm_hash: Buffer}, options?: MethodOptions) => Promise> @@ -575,9 +587,9 @@ export interface Client { /** * Construct and simulate a set_admin transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Transfer admin rights to a new address. + * Direct admin transfer (legacy, kept for backwards compatibility). * - * Admin-only. + * Admin-only. Prefer `propose_admin` + `accept_admin` for safety. */ set_admin: ({new_admin}: {new_admin: string}, options?: MethodOptions) => Promise> @@ -593,6 +605,16 @@ export interface Client { */ get_stream: ({stream_id}: {stream_id: u64}, options?: MethodOptions) => Promise> + /** + * Construct and simulate a initialize transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Initialize the contract with an admin address. + * + * Must be called exactly once before any other function. + * The intended admin must authorize the initialization to prevent + * first-caller takeover attacks. + */ + initialize: ({admin}: {admin: string}, options?: MethodOptions) => Promise> + /** * Construct and simulate a refund_max transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. * Refund the maximum refundable amount. @@ -624,6 +646,14 @@ export interface Client { */ void_stream: ({stream_id, caller}: {stream_id: u64, caller: string}, options?: MethodOptions) => Promise> + /** + * Construct and simulate a accept_admin transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Accept an admin transfer (two-step transfer, step 2). + * + * Must be called by the address that was proposed via `propose_admin()`. + */ + accept_admin: (options?: MethodOptions) => Promise> + /** * Construct and simulate a withdraw_max transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. * Withdraw the maximum available amount from a stream. @@ -632,6 +662,15 @@ export interface Client { */ withdraw_max: ({stream_id, caller, to}: {stream_id: u64, caller: string, to: string}, options?: MethodOptions) => Promise> + /** + * Construct and simulate a propose_admin transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Propose a new admin (two-step transfer, step 1). + * + * The proposed admin must call `accept_admin()` to complete the transfer. + * Admin-only. + */ + propose_admin: ({new_admin}: {new_admin: string}, options?: MethodOptions) => Promise> + /** * Construct and simulate a total_debt_of transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. * Get the total debt owed (may exceed balance). @@ -644,12 +683,38 @@ export interface Client { */ covered_debt_of: ({stream_id}: {stream_id: u64}, options?: MethodOptions) => Promise> + /** + * Construct and simulate a execute_upgrade transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Execute a previously proposed upgrade after the timelock has expired. + * + * Admin-only. + */ + execute_upgrade: ({new_wasm_hash}: {new_wasm_hash: Buffer}, options?: MethodOptions) => Promise> + + /** + * Construct and simulate a propose_upgrade transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Propose a timelocked upgrade. The upgrade can be executed after + * UPGRADE_TIMELOCK_LEDGERS have passed. + * + * Admin-only. + */ + propose_upgrade: ({new_wasm_hash}: {new_wasm_hash: Buffer}, options?: MethodOptions) => Promise> + /** * Construct and simulate a depletion_time_of transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. * Get the time at which the stream's balance will be depleted. */ depletion_time_of: ({stream_id}: {stream_id: u64}, options?: MethodOptions) => Promise> + /** + * Construct and simulate a extend_stream_ttl transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Extend the TTL of a specific stream record. + * + * Anyone can call this to keep a long-duration stream alive. + * Does not modify stream state. + */ + extend_stream_ttl: ({stream_id}: {stream_id: u64}, options?: MethodOptions) => Promise> + /** * Construct and simulate a uncovered_debt_of transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. * Get the uncovered debt (debt exceeding balance). @@ -685,8 +750,6 @@ export interface Client { } export class Client extends ContractClient { static async deploy( - /** Constructor/Initialization Args for the contract's `__constructor` method */ - {admin}: {admin: string}, /** Options for initializing a Client as well as for calling a method, with extras specific to deploying. */ options: MethodOptions & Omit & { @@ -698,7 +761,7 @@ export class Client extends ContractClient { format?: "hex" | "base64"; } ): Promise> { - return ContractClient.deploy({admin}, options) + return ContractClient.deploy(null, options) } constructor(public readonly options: ContractClientOptions) { super( @@ -707,20 +770,25 @@ export class Client extends ContractClient { "AAAAAAAAAGlSZWZ1bmQgZXhjZXNzIGJhbGFuY2UgZnJvbSBhIHN0cmVhbSBiYWNrIHRvIHRoZSBzZW5kZXIuCgpTZW5kZXItb25seS4gT25seSB1bm93ZWQgdG9rZW5zIGNhbiBiZSByZWZ1bmRlZC4AAAAAAAAGcmVmdW5kAAAAAAADAAAAAAAAAAlzdHJlYW1faWQAAAAAAAAGAAAAAAAAAAZzZW5kZXIAAAAAABMAAAAAAAAABmFtb3VudAAAAAAACwAAAAA=", "AAAAAAAAAI5EZXBvc2l0IHRva2VucyBpbnRvIGFuIGV4aXN0aW5nIHN0cmVhbS4KCkFueW9uZSBjYW4gZnVuZCBhIHN0cmVhbSwgYnV0IGBmdW5kZXIucmVxdWlyZV9hdXRoKClgIGlzIG5lZWRlZApmb3IgdGhlIHRva2VuIHRyYW5zZmVyIGF1dGhvcml6YXRpb24uAAAAAAAHZGVwb3NpdAAAAAADAAAAAAAAAAlzdHJlYW1faWQAAAAAAAAGAAAAAAAAAAZmdW5kZXIAAAAAABMAAAAAAAAABmFtb3VudAAAAAAACwAAAAA=", "AAAAAAAAAGBSZXN0YXJ0IGEgcGF1c2VkIHN0cmVhbSB3aXRoIGEgbmV3IHJhdGUuCgpTZW5kZXItb25seS4gVGhlIHN0cmVhbSBtdXN0IGJlIHBhdXNlZCBhbmQgbm90IHZvaWRlZC4AAAAHcmVzdGFydAAAAAADAAAAAAAAAAlzdHJlYW1faWQAAAAAAAAGAAAAAAAAAAZzZW5kZXIAAAAAABMAAAAAAAAAD3JhdGVfcGVyX3NlY29uZAAAAAALAAAAAA==", - "AAAAAAAAAGxVcGdyYWRlIHRoZSBjb250cmFjdCBXQVNNIGJ5dGVjb2RlLgoKQWRtaW4tb25seS4gUGVyIFNLSUxMLm1kIMKnNzogYWRtaW4tZ2F0ZWQgdXBncmFkZXMgd2l0aCBldmVudCBlbWlzc2lvbi4AAAAHdXBncmFkZQAAAAABAAAAAAAAAA1uZXdfd2FzbV9oYXNoAAAAAAAD7gAAACAAAAAA", + "AAAAAAAAAOdFbWVyZ2VuY3kgdXBncmFkZSDigJQgYnlwYXNzZXMgdGltZWxvY2suIFVzZSBvbmx5IGluIGNyaXRpY2FsIHNpdHVhdGlvbnMuCkVtaXRzIGFuIHVwZ3JhZGUgZXZlbnQgZm9yIGF1ZGl0aW5nLgoKQWRtaW4tb25seS4gQ29uc2lkZXIgcmVtb3ZpbmcgdGhpcyBmdW5jdGlvbiBhZnRlciBpbml0aWFsIG1haW5uZXQKc3RhYmlsaXphdGlvbiwgb3IgcmVxdWlyaW5nIGEgc2VwYXJhdGUgZW1lcmdlbmN5IGtleS4AAAAAB3VwZ3JhZGUAAAAAAQAAAAAAAAANbmV3X3dhc21faGFzaAAAAAAAA+4AAAAgAAAAAA==", "AAAAAAAAAKpXaXRoZHJhdyBhY2NydWVkIHRva2VucyBmcm9tIGEgc3RyZWFtLgoKT25seSB0aGUgc3RyZWFtIHJlY2lwaWVudCBjYW4gd2l0aGRyYXcuIFRoZSB3aXRoZHJhd24gYW1vdW50IGlzCmNhcHBlZCBhdCB0aGUgY292ZXJlZCBkZWJ0IChiYWxhbmNlLWJhY2tlZCBwb3J0aW9uIG9mIHRvdGFsIGRlYnQpLgAAAAAACHdpdGhkcmF3AAAABAAAAAAAAAAJc3RyZWFtX2lkAAAAAAAABgAAAAAAAAAGY2FsbGVyAAAAAAATAAAAAAAAAAJ0bwAAAAAAEwAAAAAAAAAGYW1vdW50AAAAAAALAAAAAA==", - "AAAAAAAAADRUcmFuc2ZlciBhZG1pbiByaWdodHMgdG8gYSBuZXcgYWRkcmVzcy4KCkFkbWluLW9ubHkuAAAACXNldF9hZG1pbgAAAAAAAAEAAAAAAAAACW5ld19hZG1pbgAAAAAAABMAAAAA", + "AAAAAAAAAIJEaXJlY3QgYWRtaW4gdHJhbnNmZXIgKGxlZ2FjeSwga2VwdCBmb3IgYmFja3dhcmRzIGNvbXBhdGliaWxpdHkpLgoKQWRtaW4tb25seS4gUHJlZmVyIGBwcm9wb3NlX2FkbWluYCArIGBhY2NlcHRfYWRtaW5gIGZvciBzYWZldHkuAAAAAAAJc2V0X2FkbWluAAAAAAAAAQAAAAAAAAAJbmV3X2FkbWluAAAAAAAAEwAAAAA=", "AAAAAAAAACBHZXQgdGhlIHN0cmVhbSdzIGN1cnJlbnQgc3RhdHVzLgAAAAlzdGF0dXNfb2YAAAAAAAABAAAAAAAAAAlzdHJlYW1faWQAAAAAAAAGAAAAAQAAB9AAAAAMU3RyZWFtU3RhdHVz", "AAAAAAAAABtHZXQgdGhlIGZ1bGwgc3RyZWFtIHJlY29yZC4AAAAACmdldF9zdHJlYW0AAAAAAAEAAAAAAAAACXN0cmVhbV9pZAAAAAAAAAYAAAABAAAH0AAAAApGbG93U3RyZWFtAAA=", + "AAAAAAAAAMVJbml0aWFsaXplIHRoZSBjb250cmFjdCB3aXRoIGFuIGFkbWluIGFkZHJlc3MuCgpNdXN0IGJlIGNhbGxlZCBleGFjdGx5IG9uY2UgYmVmb3JlIGFueSBvdGhlciBmdW5jdGlvbi4KVGhlIGludGVuZGVkIGFkbWluIG11c3QgYXV0aG9yaXplIHRoZSBpbml0aWFsaXphdGlvbiB0byBwcmV2ZW50CmZpcnN0LWNhbGxlciB0YWtlb3ZlciBhdHRhY2tzLgAAAAAAAAppbml0aWFsaXplAAAAAAABAAAAAAAAAAVhZG1pbgAAAAAAABMAAAAA", "AAAAAAAAAFBSZWZ1bmQgdGhlIG1heGltdW0gcmVmdW5kYWJsZSBhbW91bnQuCgpTZW5kZXItb25seS4gUmV0dXJucyB0aGUgYW1vdW50IHJlZnVuZGVkLgAAAApyZWZ1bmRfbWF4AAAAAAACAAAAAAAAAAlzdHJlYW1faWQAAAAAAAAGAAAAAAAAAAZzZW5kZXIAAAAAABMAAAABAAAACw==", "AAAAAAAAAHhBZGp1c3QgdGhlIHJhdGUgcGVyIHNlY29uZCBvZiBhbiBhY3RpdmUgc3RyZWFtLgoKU2VuZGVyLW9ubHkuIFRoZSBzdHJlYW0gbXVzdCBiZSBhY3RpdmVseSBzdHJlYW1pbmcgKG5vdCBwYXVzZWQvdm9pZGVkKS4AAAALYWRqdXN0X3JhdGUAAAAAAwAAAAAAAAAJc3RyZWFtX2lkAAAAAAAABgAAAAAAAAAGc2VuZGVyAAAAAAATAAAAAAAAAAhuZXdfcmF0ZQAAAAsAAAAA", "AAAAAAAAACFHZXQgdGhlIHN0cmVhbSdzIGN1cnJlbnQgYmFsYW5jZS4AAAAAAAALZ2V0X2JhbGFuY2UAAAAAAQAAAAAAAAAJc3RyZWFtX2lkAAAAAAAABgAAAAEAAAAL", "AAAAAAAAAIRQZXJtYW5lbnRseSB2b2lkIGEgc3RyZWFtLgoKQ2FsbGFibGUgYnkgc2VuZGVyIE9SIHJlY2lwaWVudC4gV3JpdGVzIG9mZiB1bmNvdmVyZWQgZGVidCBhbmQKcHJldmVudHMgdGhlIHN0cmVhbSBmcm9tIGJlaW5nIHJlc3RhcnRlZC4AAAALdm9pZF9zdHJlYW0AAAAAAgAAAAAAAAAJc3RyZWFtX2lkAAAAAAAABgAAAAAAAAAGY2FsbGVyAAAAAAATAAAAAA==", + "AAAAAAAAAH1BY2NlcHQgYW4gYWRtaW4gdHJhbnNmZXIgKHR3by1zdGVwIHRyYW5zZmVyLCBzdGVwIDIpLgoKTXVzdCBiZSBjYWxsZWQgYnkgdGhlIGFkZHJlc3MgdGhhdCB3YXMgcHJvcG9zZWQgdmlhIGBwcm9wb3NlX2FkbWluKClgLgAAAAAAAAxhY2NlcHRfYWRtaW4AAAAAAAAAAA==", "AAAAAAAAAHFXaXRoZHJhdyB0aGUgbWF4aW11bSBhdmFpbGFibGUgYW1vdW50IGZyb20gYSBzdHJlYW0uCgpDb252ZW5pZW5jZSBmdW5jdGlvbiDigJQgd2l0aGRyYXdzIHRoZSBlbnRpcmUgY292ZXJlZCBkZWJ0LgAAAAAAAAx3aXRoZHJhd19tYXgAAAADAAAAAAAAAAlzdHJlYW1faWQAAAAAAAAGAAAAAAAAAAZjYWxsZXIAAAAAABMAAAAAAAAAAnRvAAAAAAATAAAAAQAAAAs=", - "AAAAAAAAADVBdG9taWNhbGx5IGluaXRpYWxpemUgdGhlIGNvbnRyYWN0IGR1cmluZyBkZXBsb3ltZW50LgAAAAAAAA1fX2NvbnN0cnVjdG9yAAAAAAAAAQAAAAAAAAAFYWRtaW4AAAAAAAATAAAAAA==", + "AAAAAAAAAIVQcm9wb3NlIGEgbmV3IGFkbWluICh0d28tc3RlcCB0cmFuc2Zlciwgc3RlcCAxKS4KClRoZSBwcm9wb3NlZCBhZG1pbiBtdXN0IGNhbGwgYGFjY2VwdF9hZG1pbigpYCB0byBjb21wbGV0ZSB0aGUgdHJhbnNmZXIuCkFkbWluLW9ubHkuAAAAAAAADXByb3Bvc2VfYWRtaW4AAAAAAAABAAAAAAAAAAluZXdfYWRtaW4AAAAAAAATAAAAAA==", "AAAAAAAAAC1HZXQgdGhlIHRvdGFsIGRlYnQgb3dlZCAobWF5IGV4Y2VlZCBiYWxhbmNlKS4AAAAAAAANdG90YWxfZGVidF9vZgAAAAAAAAEAAAAAAAAACXN0cmVhbV9pZAAAAAAAAAYAAAABAAAACw==", "AAAAAAAAAC5HZXQgdGhlIGNvdmVyZWQgZGVidCAoZGVidCBiYWNrZWQgYnkgYmFsYW5jZSkuAAAAAAAPY292ZXJlZF9kZWJ0X29mAAAAAAEAAAAAAAAACXN0cmVhbV9pZAAAAAAAAAYAAAABAAAACw==", + "AAAAAAAAAFJFeGVjdXRlIGEgcHJldmlvdXNseSBwcm9wb3NlZCB1cGdyYWRlIGFmdGVyIHRoZSB0aW1lbG9jayBoYXMgZXhwaXJlZC4KCkFkbWluLW9ubHkuAAAAAAAPZXhlY3V0ZV91cGdyYWRlAAAAAAEAAAAAAAAADW5ld193YXNtX2hhc2gAAAAAAAPuAAAAIAAAAAA=", + "AAAAAAAAAHJQcm9wb3NlIGEgdGltZWxvY2tlZCB1cGdyYWRlLiBUaGUgdXBncmFkZSBjYW4gYmUgZXhlY3V0ZWQgYWZ0ZXIKVVBHUkFERV9USU1FTE9DS19MRURHRVJTIGhhdmUgcGFzc2VkLgoKQWRtaW4tb25seS4AAAAAAA9wcm9wb3NlX3VwZ3JhZGUAAAAAAQAAAAAAAAANbmV3X3dhc21faGFzaAAAAAAAA+4AAAAgAAAAAA==", "AAAAAAAAADxHZXQgdGhlIHRpbWUgYXQgd2hpY2ggdGhlIHN0cmVhbSdzIGJhbGFuY2Ugd2lsbCBiZSBkZXBsZXRlZC4AAAARZGVwbGV0aW9uX3RpbWVfb2YAAAAAAAABAAAAAAAAAAlzdHJlYW1faWQAAAAAAAAGAAAAAQAAAAY=", + "AAAAAAAAAIVFeHRlbmQgdGhlIFRUTCBvZiBhIHNwZWNpZmljIHN0cmVhbSByZWNvcmQuCgpBbnlvbmUgY2FuIGNhbGwgdGhpcyB0byBrZWVwIGEgbG9uZy1kdXJhdGlvbiBzdHJlYW0gYWxpdmUuCkRvZXMgbm90IG1vZGlmeSBzdHJlYW0gc3RhdGUuAAAAAAAAEWV4dGVuZF9zdHJlYW1fdHRsAAAAAAAAAQAAAAAAAAAJc3RyZWFtX2lkAAAAAAAABgAAAAA=", "AAAAAAAAADBHZXQgdGhlIHVuY292ZXJlZCBkZWJ0IChkZWJ0IGV4Y2VlZGluZyBiYWxhbmNlKS4AAAARdW5jb3ZlcmVkX2RlYnRfb2YAAAAAAAABAAAAAAAAAAlzdHJlYW1faWQAAAAAAAAGAAAAAQAAAAs=", "AAAAAAAAAHJDcmVhdGUgYSBuZXcgRmxvdyBzdHJlYW0gYW5kIGltbWVkaWF0ZWx5IGRlcG9zaXQgdG9rZW5zLgoKQ29udmVuaWVuY2UgZnVuY3Rpb24gY29tYmluaW5nIGBjcmVhdGUoKWAgKyBgZGVwb3NpdCgpYC4AAAAAABJjcmVhdGVfYW5kX2RlcG9zaXQAAAAAAAcAAAAAAAAABnNlbmRlcgAAAAAAEwAAAAAAAAAJcmVjaXBpZW50AAAAAAAAEwAAAAAAAAAFdG9rZW4AAAAAAAATAAAAAAAAAA9yYXRlX3Blcl9zZWNvbmQAAAAACwAAAAAAAAAOdG9rZW5fZGVjaW1hbHMAAAAAAAQAAAAAAAAACnN0YXJ0X3RpbWUAAAAAAAYAAAAAAAAABmFtb3VudAAAAAAACwAAAAEAAAAG", "AAAAAAAAACFHZXQgdGhlIHN0cmVhbSdzIHJhdGUgcGVyIHNlY29uZC4AAAAAAAATZ2V0X3JhdGVfcGVyX3NlY29uZAAAAAABAAAAAAAAAAlzdHJlYW1faWQAAAAAAAAGAAAAAQAAAAs=", @@ -731,14 +799,12 @@ export class Client extends ContractClient { "AAAAAwAAAMtTdGF0dXMgb2YgYSBMb2NrdXAgc3RyZWFtLgoKVXNlcyAidGVtcGVyYXR1cmUiIHNlbWFudGljczoKLSAqKldhcm0qKiAoUGVuZGluZywgU3RyZWFtaW5nKTogdGltZSBhbG9uZSBjYW4gY2hhbmdlIHRoZSBzdGF0dXMuCi0gKipDb2xkKiogKFNldHRsZWQsIENhbmNlbGVkLCBEZXBsZXRlZCk6IHRpbWUgYWxvbmUgY2Fubm90IGNoYW5nZSB0aGUgc3RhdHVzLgAAAAAAAAAADExvY2t1cFN0YXR1cwAAAAUAAAA/Q3JlYXRlZCBidXQgc3RhcnRfdGltZSBpcyBpbiB0aGUgZnV0dXJlLiBObyB0b2tlbnMgaGF2ZSB2ZXN0ZWQuAAAAAAdQZW5kaW5nAAAAAAAAAAAyQWN0aXZlIOKAlCB0b2tlbnMgYXJlIGN1cnJlbnRseSB2ZXN0aW5nIG92ZXIgdGltZS4AAAAAAAlTdHJlYW1pbmcAAAAAAAABAAAAS0FsbCB0b2tlbnMgaGF2ZSBmdWxseSB2ZXN0ZWQuIFJlY2lwaWVudCBjYW4gd2l0aGRyYXcgdGhlIHJlbWFpbmluZyBiYWxhbmNlLgAAAAAHU2V0dGxlZAAAAAACAAAAP1NlbmRlciBjYW5jZWxlZCB0aGUgc3RyZWFtLiBVbnZlc3RlZCB0b2tlbnMgcmV0dXJuZWQgdG8gc2VuZGVyLgAAAAAIQ2FuY2VsZWQAAAADAAAAQkZ1bGx5IHdpdGhkcmF3biAoYW5kL29yIHJlZnVuZGVkKS4gTm8gdG9rZW5zIHJlbWFpbiBpbiB0aGUgc3RyZWFtLgAAAAAACERlcGxldGVkAAAABA==", "AAAAAQAAAtxDb3JlIGRhdGEgc3RydWN0dXJlIGZvciBhIExvY2t1cCAoZml4ZWQtdGVybSB2ZXN0aW5nKSBzdHJlYW0uCgpTdXBwb3J0cyBsaW5lYXIgdW5sb2NrIHdpdGggb3B0aW9uYWwgY2xpZmYuIFRoZSB2ZXN0ZWQgKCJzdHJlYW1lZCIpIGFtb3VudAphdCBhbnkgdGltZSBgdGAgaXMgY2FsY3VsYXRlZCBhczoKCmBgYHRleHQKaWYgdCA8IHN0YXJ0X3RpbWU6ICAgICAgIHZlc3RlZCA9IDAKaWYgdCA8IGNsaWZmX3RpbWU6ICAgICAgIHZlc3RlZCA9IHN0YXJ0X3VubG9ja19hbW91bnQKaWYgdCA+PSBlbmRfdGltZTogICAgICAgICB2ZXN0ZWQgPSB0b3RhbF9hbW91bnQKZWxzZToKZWxhcHNlZCA9IGZsb29yKCh0IC0gY2xpZmZfdGltZSkgLyBncmFudWxhcml0eSkgKiBncmFudWxhcml0eQpzdHJlYW1hYmxlX2R1cmF0aW9uID0gZW5kX3RpbWUgLSBjbGlmZl90aW1lCnN0cmVhbWFibGVfYW1vdW50ID0gdG90YWxfYW1vdW50IC0gc3RhcnRfdW5sb2NrX2Ftb3VudCAtIGNsaWZmX3VubG9ja19hbW91bnQKdmVzdGVkID0gc3RhcnRfdW5sb2NrX2Ftb3VudCArIGNsaWZmX3VubG9ja19hbW91bnQgKyAoZWxhcHNlZCAqIHN0cmVhbWFibGVfYW1vdW50IC8gc3RyZWFtYWJsZV9kdXJhdGlvbikKYGBgCgpUaGlzIG1pcnJvcnMgdGhlIHJlZmVyZW5jZSBsaW5lYXIgbG9ja3VwIGNhbGN1bGF0aW9uIHdpdGggZGlzY3JldGUgdW5sb2NrCnN0ZXBzIGF0IGBncmFudWxhcml0eWAtc2Vjb25kIGludGVydmFscy4AAAAAAAAADExvY2t1cFN0cmVhbQAAAA8AAABGV2hldGhlciB0aGUgc2VuZGVyIGNhbiBjYW5jZWwgdGhpcyBzdHJlYW0gYW5kIHJlY2xhaW0gdW52ZXN0ZWQgdG9rZW5zLgAAAAAACmNhbmNlbGFibGUAAAAAAAEAAABuT3B0aW9uYWwgY2xpZmYgdGltZXN0YW1wLiBObyB0b2tlbnMgYmV5b25kIGBzdGFydF91bmxvY2tfYW1vdW50YAp2ZXN0IGJlZm9yZSB0aGlzIHRpbWUuIFNldCB0byAwIGZvciBubyBjbGlmZi4AAAAAAApjbGlmZl90aW1lAAAAAAAGAAAAR0Ftb3VudCB1bmxvY2tlZCBhdCBgY2xpZmZfdGltZWAgKGluIGFkZGl0aW9uIHRvIGBzdGFydF91bmxvY2tfYW1vdW50YCkuAAAAABNjbGlmZl91bmxvY2tfYW1vdW50AAAAAAsAAAAxVW5peCB0aW1lc3RhbXAgd2hlbiB0aGUgbG9ja3VwIGlzIGZ1bGx5IHVubG9ja2VkLgAAAAAAAAhlbmRfdGltZQAAAAYAAAB9VW5sb2NrIGdyYW51bGFyaXR5IGluIHNlY29uZHMuIFRva2VucyB2ZXN0IGluIGRpc2NyZXRlIHN0ZXBzIG9mIHRoaXMKaW50ZXJ2YWwuIERlZmF1bHQgPSAxIChwZXItc2Vjb25kIHZlc3RpbmcpLiBNdXN0IGJlID4gMC4AAAAAAAALZ3JhbnVsYXJpdHkAAAAABgAAADdXaGV0aGVyIGFsbCB0b2tlbnMgaGF2ZSBiZWVuIHdpdGhkcmF3biBhbmQvb3IgcmVmdW5kZWQuAAAAAAtpc19kZXBsZXRlZAAAAAABAAAAP1RoZSBhZGRyZXNzIHRoYXQgcmVjZWl2ZXMgdG9rZW5zIGFzIHRoZXkgdW5sb2NrIChjYW4gd2l0aGRyYXcpLgAAAAAJcmVjaXBpZW50AAAAAAAAEwAAAEFBbW91bnQgcmVmdW5kZWQgdG8gc2VuZGVyIG9uIGNhbmNlbGxhdGlvbi4gWmVybyB1bmxlc3MgY2FuY2VsbGVkLgAAAAAAAA9yZWZ1bmRlZF9hbW91bnQAAAAACwAAAExUaGUgYWRkcmVzcyB0aGF0IGNyZWF0ZWQgYW5kIGZ1bmRlZCB0aGUgbG9ja3VwIChjYW4gY2FuY2VsIGlmIGBjYW5jZWxhYmxlYCkuAAAABnNlbmRlcgAAAAAAEwAAACZVbml4IHRpbWVzdGFtcCB3aGVuIHRoZSBsb2NrdXAgYmVnaW5zLgAAAAAACnN0YXJ0X3RpbWUAAAAAAAYAAAAsQW1vdW50IHVubG9ja2VkIGltbWVkaWF0ZWx5IGF0IGBzdGFydF90aW1lYC4AAAATc3RhcnRfdW5sb2NrX2Ftb3VudAAAAAALAAAAI1RoZSBTb3JvYmFuIHRva2VuIGNvbnRyYWN0IGFkZHJlc3MuAAAAAAV0b2tlbgAAAAAAABMAAAA7VG90YWwgYW1vdW50IGRlcG9zaXRlZCBpbnRvIHRoZSBzdHJlYW0gKGluIHRva2VuIGRlY2ltYWxzKS4AAAAADHRvdGFsX2Ftb3VudAAAAAsAAAAmV2hldGhlciB0aGUgc3RyZWFtIGhhcyBiZWVuIGNhbmNlbGxlZC4AAAAAAAx3YXNfY2FuY2VsZWQAAAABAAAALUN1bXVsYXRpdmUgYW1vdW50IHdpdGhkcmF3biBieSB0aGUgcmVjaXBpZW50LgAAAAAAABB3aXRoZHJhd25fYW1vdW50AAAACw==", "AAAAAwAAALFTdGF0dXMgb2YgYSBGbG93IHN0cmVhbS4KClRoZSBzdGF0dXMgaXMgZGVyaXZlZCBmcm9tIHRoZSBzdHJlYW0ncyBjdXJyZW50IHN0YXRlIChyYXRlLCBiYWxhbmNlLCBkZWJ0LCB2b2lkZWQgZmxhZykgcmF0aGVyIHRoYW4KYmVpbmcgc3RvcmVkIGRpcmVjdGx5IOKAlCBrZWVwaW5nIHN0b3JhZ2UgbWluaW1hbC4AAAAAAAAAAAAADFN0cmVhbVN0YXR1cwAAAAYAAAA+U3RyZWFtIHNjaGVkdWxlZCB0byBzdGFydCBpbiB0aGUgZnV0dXJlIChzbmFwc2hvdF90aW1lID4gbm93KS4AAAAAAAdQZW5kaW5nAAAAAAAAAAAyQWN0aXZlbHkgc3RyZWFtaW5nIHdpdGggYmFsYW5jZSBjb3ZlcmluZyBhbGwgZGVidC4AAAAAABBTdHJlYW1pbmdTb2x2ZW50AAAAAQAAADZBY3RpdmVseSBzdHJlYW1pbmcgYnV0IGRlYnQgZXhjZWVkcyBhdmFpbGFibGUgYmFsYW5jZS4AAAAAABJTdHJlYW1pbmdJbnNvbHZlbnQAAAAAAAIAAAAoUGF1c2VkIGJ5IHNlbmRlciB3aXRoIG5vIHVuY292ZXJlZCBkZWJ0LgAAAA1QYXVzZWRTb2x2ZW50AAAAAAAAAwAAACVQYXVzZWQgYnkgc2VuZGVyIHdpdGggdW5jb3ZlcmVkIGRlYnQuAAAAAAAAD1BhdXNlZEluc29sdmVudAAAAAAEAAAARVBlcm1hbmVudGx5IHN0b3BwZWQuIENhbm5vdCBiZSByZXN0YXJ0ZWQuIFVuY292ZXJlZCBkZWJ0IHdyaXR0ZW4gb2ZmLgAAAAAAAAZWb2lkZWQAAAAAAAU=", - "AAAAAQAAAEdTdGFibGUgcHVibGljIG1ldGFkYXRhIGZvciBhIFN0cmVhbSBORlQgYW5kIGl0cyB1bmRlcmx5aW5nIGNvcmUgc3RyZWFtLgAAAAAAAAAADlN0cmVhbU1ldGFkYXRhAAAAAAAGAAAAOUludGVybmFsIGlkZW50aWZpZXIgYXNzaWduZWQgYnkgdGhlIHNlbGVjdGVkIGNvcmUgZW5naW5lLgAAAAAAAA5jb3JlX3N0cmVhbV9pZAAAAAAABgAAADxDdXJyZW50IGhvbGRlciBvZiB3aXRoZHJhd2FsIGFuZCBORlQtb3duZXIgRmxvdyB2b2lkIHJpZ2h0cy4AAAAFb3duZXIAAAAAAAATAAAAJ0Nhbm9uaWNhbCBjcm9zcy1lbmdpbmUgbGlmZWN5Y2xlIHN0YXRlLgAAAAAGc3RhdHVzAAAAAAfQAAAAFUNhbm9uaWNhbFN0cmVhbVN0YXR1cwAAAAAAABFDb3JlIGVuZ2luZSBraW5kLgAAAAAAAAtzdHJlYW1fdHlwZQAAAAfQAAAAClN0cmVhbVR5cGUAAAAAACxQdWJsaWMsIGRlcGxveW1lbnQtc2NvcGVkIHN0cmVhbSBpZGVudGlmaWVyLgAAAAh0b2tlbl9pZAAAAAsAAABFSW1tdXRhYmxlIHRyYW5zZmVyYWJpbGl0eSBwb2xpY3kgc2VsZWN0ZWQgd2hlbiB0aGUgc3RyZWFtIGlzIGNyZWF0ZWQuAAAAAAAADHRyYW5zZmVyYWJsZQAAAAE=", "AAAAAQAAAINQYXJhbWV0ZXJzIGZvciBjcmVhdGluZyBhIG5ldyBMb2NrdXAgc3RyZWFtLgoKQnVuZGxlZCBpbnRvIGEgc3RydWN0IGJlY2F1c2UgU29yb2JhbiBjb250cmFjdCBmdW5jdGlvbnMKaGF2ZSBhIG1heCBvZiAxMCBwYXJhbWV0ZXJzLgAAAAAAAAAAEkNyZWF0ZUxvY2t1cFBhcmFtcwAAAAAACwAAAClXaGV0aGVyIHRoZSBzZW5kZXIgY2FuIGNhbmNlbCB0aGUgc3RyZWFtLgAAAAAAAApjYW5jZWxhYmxlAAAAAAABAAAAME9wdGlvbmFsIGNsaWZmIHRpbWVzdGFtcC4gU2V0IHRvIDAgZm9yIG5vIGNsaWZmLgAAAApjbGlmZl90aW1lAAAAAAAGAAAAKlRva2VucyB1bmxvY2tlZCBhdCBjbGlmZiAoYWRkZWQgdG8gc3RhcnQpLgAAAAAAE2NsaWZmX3VubG9ja19hbW91bnQAAAAACwAAACZVbml4IHRpbWVzdGFtcCB3aGVuIHZlc3RpbmcgY29tcGxldGVzLgAAAAAACGVuZF90aW1lAAAABgAAADFVbmxvY2sgc3RlcCBpbnRlcnZhbCBpbiBzZWNvbmRzLiAwIGRlZmF1bHRzIHRvIDEuAAAAAAAAC2dyYW51bGFyaXR5AAAAAAYAAAAgQWRkcmVzcyByZWNlaXZpbmcgdmVzdGVkIHRva2Vucy4AAAAJcmVjaXBpZW50AAAAAAAAEwAAADhBZGRyZXNzIGZ1bmRpbmcgdGhlIHN0cmVhbSAoY2FuIGNhbmNlbCBpZiBgY2FuY2VsYWJsZWApLgAAAAZzZW5kZXIAAAAAABMAAAAjVW5peCB0aW1lc3RhbXAgd2hlbiB2ZXN0aW5nIGJlZ2lucy4AAAAACnN0YXJ0X3RpbWUAAAAAAAYAAAAlVG9rZW5zIHVubG9ja2VkIGltbWVkaWF0ZWx5IGF0IHN0YXJ0LgAAAAAAABNzdGFydF91bmxvY2tfYW1vdW50AAAAAAsAAAAfU29yb2JhbiB0b2tlbiBjb250cmFjdCBhZGRyZXNzLgAAAAAFdG9rZW4AAAAAAAATAAAAKVRvdGFsIHRva2VucyB0byB2ZXN0IChpbiB0b2tlbiBkZWNpbWFscykuAAAAAAAADHRvdGFsX2Ftb3VudAAAAAs=", - "AAAAAwAAAMZDYW5vbmljYWwgbGlmZWN5Y2xlIGV4cG9zZWQgYnkgdGhlIFJvdXRlciB0byBldmVyeSBwcm9kdWN0IHN1cmZhY2UuCgpDb3JlIGVuZ2luZXMgcmV0YWluIHRoZWlyIG1vcmUgZGV0YWlsZWQgYWNjb3VudGluZyBzdGF0ZXM7IHRoZSBSb3V0ZXIgbWFwcwp0aG9zZSBzdGF0ZXMgaW50byB0aGlzIHN0YWJsZSBjcm9zcy1lbmdpbmUgdm9jYWJ1bGFyeS4AAAAAAAAAAAAVQ2Fub25pY2FsU3RyZWFtU3RhdHVzAAAAAAAABgAAAAAAAAAHUGVuZGluZwAAAAAAAAAAAAAAAAZBY3RpdmUAAAAAAAEAAAAAAAAABlBhdXNlZAAAAAAAAgAAAAAAAAAIQ2FuY2VsZWQAAAADAAAAAAAAAAlDb21wbGV0ZWQAAAAAAAAEAAAAAAAAAAZGYWlsZWQAAAAAAAU=", "AAAABAAAACtFcnJvcnMgc3BlY2lmaWMgdG8gdGhlIFN0cmVhbSBORlQgY29udHJhY3QuAAAAAAAAAAAITmZ0RXJyb3IAAAAFAAAAAAAAABJBbHJlYWR5SW5pdGlhbGl6ZWQAAAAAAMkAAAAAAAAADU5vdEF1dGhvcml6ZWQAAAAAAADKAAAAAAAAAA1Ub2tlbk5vdEZvdW5kAAAAAAAAywAAAAAAAAAPTm90VHJhbnNmZXJhYmxlAAAAAMwAAAAhVG9rZW4gSUQgaGFzIGFscmVhZHkgYmVlbiBtaW50ZWQuAAAAAAAADUFscmVhZHlNaW50ZWQAAAAAAADN", - "AAAABAAAAC5FcnJvcnMgZW1pdHRlZCBieSB0aGUgRmxvdyBzdHJlYW1pbmcgY29udHJhY3QuAAAAAAAAAAAACUZsb3dFcnJvcgAAAAAAABYAAAAZU3RyZWFtIElEIGRvZXMgbm90IGV4aXN0LgAAAAAAAA5TdHJlYW1Ob3RGb3VuZAAAAAAAAQAAACxDYWxsZXIgaXMgbm90IGF1dGhvcml6ZWQgZm9yIHRoaXMgb3BlcmF0aW9uLgAAAAxVbmF1dGhvcml6ZWQAAAACAAAANlN0cmVhbSBpcyBwYXVzZWQ7IG9wZXJhdGlvbiByZXF1aXJlcyBhY3RpdmUgc3RyZWFtaW5nLgAAAAAADFN0cmVhbVBhdXNlZAAAAAMAAAAvU3RyZWFtIGlzIHZvaWRlZDsgbm8gZnVydGhlciBtdXRhdGlvbnMgYWxsb3dlZC4AAAAADFN0cmVhbVZvaWRlZAAAAAQAAAA3U3RyZWFtIGlzIG5vdCBwYXVzZWQ7IHJlc3RhcnQgcmVxdWlyZXMgYSBwYXVzZWQgc3RyZWFtLgAAAAAPU3RyZWFtTm90UGF1c2VkAAAAAAUAAAA5U3RyZWFtIGhhcyBub3Qgc3RhcnRlZCB5ZXQgKHNuYXBzaG90X3RpbWUgaW4gdGhlIGZ1dHVyZSkuAAAAAAAADVN0cmVhbVBlbmRpbmcAAAAAAAAGAAAAIE5ldyByYXRlIHBlciBzZWNvbmQgbXVzdCBiZSA+IDAuAAAAEVJhdGVQZXJTZWNvbmRaZXJvAAAAAAAABwAAACdOZXcgcmF0ZSBtdXN0IGRpZmZlciBmcm9tIGN1cnJlbnQgcmF0ZS4AAAAAEFJhdGVOb3REaWZmZXJlbnQAAAAIAAAAG0RlcG9zaXQgYW1vdW50IG11c3QgYmUgPiAwLgAAAAARRGVwb3NpdEFtb3VudFplcm8AAAAAAAAJAAAAHFdpdGhkcmF3IGFtb3VudCBtdXN0IGJlID4gMC4AAAASV2l0aGRyYXdBbW91bnRaZXJvAAAAAAAKAAAALVdpdGhkcmF3IGFtb3VudCBleGNlZWRzIHdpdGhkcmF3YWJsZSBiYWxhbmNlLgAAAAAAAAhPdmVyZHJhdwAAAAsAAAAaUmVmdW5kIGFtb3VudCBtdXN0IGJlID4gMC4AAAAAABBSZWZ1bmRBbW91bnRaZXJvAAAADAAAAClSZWZ1bmQgYW1vdW50IGV4Y2VlZHMgcmVmdW5kYWJsZSBiYWxhbmNlLgAAAAAAAA5SZWZ1bmRPdmVyZmxvdwAAAAAADQAAACVUb2tlbiBoYXMgPiAxOCBkZWNpbWFscywgdW5zdXBwb3J0ZWQuAAAAAAAAFEludmFsaWRUb2tlbkRlY2ltYWxzAAAADgAAADZTdHJlYW0gYmFsYW5jZSBpcyB6ZXJvIChlLmcuIHF1ZXJ5aW5nIGRlcGxldGlvbiB0aW1lKS4AAAAAAAtCYWxhbmNlWmVybwAAAAAPAAAAHUNvbnRyYWN0IGFscmVhZHkgaW5pdGlhbGl6ZWQuAAAAAAAAEkFscmVhZHlJbml0aWFsaXplZAAAAAAAEAAAAB1Db250cmFjdCBub3QgeWV0IGluaXRpYWxpemVkLgAAAAAAAA5Ob3RJbml0aWFsaXplZAAAAAAAEQAAADhDYW5ub3QgY3JlYXRlIGEgcGVuZGluZyBzdHJlYW0gd2l0aCByYXRlX3Blcl9zZWNvbmQgPSAwLgAAABdDcmVhdGVSYXRlUGVyU2Vjb25kWmVybwAAAAASAAAAOUludGVybmFsIG1hdGggZXJyb3Ig4oCUIHNob3VsZCBuZXZlciBvY2N1ciBpbiBwcm9kdWN0aW9uLgAAAAAAABJJbnZhbGlkQ2FsY3VsYXRpb24AAAAAABMAAAAxU2VuZGVyIGFuZCByZWNpcGllbnQgbXVzdCBiZSBkaWZmZXJlbnQgYWRkcmVzc2VzLgAAAAAAABVTZW5kZXJFcXVhbHNSZWNpcGllbnQAAAAAAAAUAAAAJVJhdGUgcGVyIHNlY29uZCBtdXN0IG5vdCBiZSBuZWdhdGl2ZS4AAAAAAAAMTmVnYXRpdmVSYXRlAAAAFQAAAD5UaGUgdG9rZW4gZGlkIG5vdCBkZWJpdCBhbmQgY3JlZGl0IHRoZSBleGFjdCByZXF1ZXN0ZWQgYW1vdW50LgAAAAAAFVRva2VuVHJhbnNmZXJNaXNtYXRjaAAAAAAAABY=", - "AAAABAAAAC5FcnJvcnMgZW1pdHRlZCBieSB0aGUgTG9ja3VwIHZlc3RpbmcgY29udHJhY3QuAAAAAAAAAAAAC0xvY2t1cEVycm9yAAAAAAwAAAAZU3RyZWFtIElEIGRvZXMgbm90IGV4aXN0LgAAAAAAAA5TdHJlYW1Ob3RGb3VuZAAAAAAAZQAAABlDYWxsZXIgaXMgbm90IGF1dGhvcml6ZWQuAAAAAAAADFVuYXV0aG9yaXplZAAAAGYAAAAcU3RyZWFtIGlzIGFscmVhZHkgY2FuY2VsbGVkLgAAABBBbHJlYWR5Q2FuY2VsbGVkAAAAZwAAABlTdHJlYW0gaXMgbm90IGNhbmNlbGFibGUuAAAAAAAADU5vdENhbmNlbGFibGUAAAAAAABoAAAAKVdpdGhkcmF3IGFtb3VudCBleGNlZWRzIHVubG9ja2VkIGJhbGFuY2UuAAAAAAAACE92ZXJkcmF3AAAAaQAAADxJbnZhbGlkIHRpbWUgcGFyYW1ldGVycyAoc3RhcnQgPj0gZW5kLCBjbGlmZiBvdXRzaWRlIHJhbmdlKS4AAAAQSW52YWxpZFRpbWVSYW5nZQAAAGoAAAAZVG90YWwgYW1vdW50IG11c3QgYmUgPiAwLgAAAAAAAApBbW91bnRaZXJvAAAAAABrAAAAHUNvbnRyYWN0IGFscmVhZHkgaW5pdGlhbGl6ZWQuAAAAAAAAEkFscmVhZHlJbml0aWFsaXplZAAAAAAAbAAAAB1Db250cmFjdCBub3QgeWV0IGluaXRpYWxpemVkLgAAAAAAAA5Ob3RJbml0aWFsaXplZAAAAAAAbQAAADFTZW5kZXIgYW5kIHJlY2lwaWVudCBtdXN0IGJlIGRpZmZlcmVudCBhZGRyZXNzZXMuAAAAAAAAFVNlbmRlckVxdWFsc1JlY2lwaWVudAAAAAAAAG4AAABLU3RhcnQgYW5kIGNsaWZmIHVubG9jayBhbW91bnRzIG11c3QgYmUgbm9ubmVnYXRpdmUgYW5kIGZpdCBpbiB0b3RhbCBhbW91bnQuAAAAABNJbnZhbGlkVW5sb2NrQW1vdW50AAAAAG8AAAA+VGhlIHRva2VuIGRpZCBub3QgZGViaXQgYW5kIGNyZWRpdCB0aGUgZXhhY3QgcmVxdWVzdGVkIGFtb3VudC4AAAAAABVUb2tlblRyYW5zZmVyTWlzbWF0Y2gAAAAAAABw", - "AAAABAAAACdFcnJvcnMgc3BlY2lmaWMgdG8gdGhlIFJvdXRlciBjb250cmFjdC4AAAAAAAAAAAtSb3V0ZXJFcnJvcgAAAAAFAAAAAAAAABJBbHJlYWR5SW5pdGlhbGl6ZWQAAAAAAS0AAAAAAAAADk5vdEluaXRpYWxpemVkAAAAAAEuAAAAAAAAAA1Ob3RBdXRob3JpemVkAAAAAAABLwAAAAAAAAARSW52YWxpZFN0cmVhbVR5cGUAAAAAAAEwAAAAMENvcmUgY29udHJhY3QgYWRkcmVzc2VzIHdlcmUgYWxyZWFkeSBjb25maWd1cmVkLgAAABFBbHJlYWR5Q29uZmlndXJlZAAAAAAAATE=", - "AAAAAgAAAKFTdG9yYWdlIGtleXMgZm9yIGNvbnRyYWN0IGRhdGEuCgpVc2luZyBhIHR5cGVkIGVudW0gcHJldmVudHMga2V5IGNvbGxpc2lvbnMgKFNLSUxMLm1kIMKnMykuCktleXMgYXJlIG5hbWVzcGFjZWQgYnkgdmFyaWFudCB0byBrZWVwIGRpZmZlcmVudCBkYXRhIHR5cGVzIHNlcGFyYXRlLgAAAAAAAAAAAAAHRGF0YUtleQAAAAAPAAAAAAAAACFBZG1pbiBhZGRyZXNzIChJbnN0YW5jZSBzdG9yYWdlKS4AAAAAAAAFQWRtaW4AAAAAAAAAAAAAKk5leHQgc3RyZWFtIElEIGNvdW50ZXIgKEluc3RhbmNlIHN0b3JhZ2UpLgAAAAAADE5leHRTdHJlYW1JZAAAAAEAAAA+QSBGbG93IHN0cmVhbSByZWNvcmQsIGtleWVkIGJ5IHN0cmVhbSBJRCAoUGVyc2lzdGVudCBzdG9yYWdlKS4AAAAAAApGbG93U3RyZWFtAAAAAAABAAAABgAAAAEAAABAQSBMb2NrdXAgc3RyZWFtIHJlY29yZCwga2V5ZWQgYnkgc3RyZWFtIElEIChQZXJzaXN0ZW50IHN0b3JhZ2UpLgAAAAxMb2NrdXBTdHJlYW0AAAABAAAABgAAAAEAAACTQWdncmVnYXRlIHRva2VuIGJhbGFuY2UgaGVsZCBieSB0aGUgY29udHJhY3QgZm9yIGEgZ2l2ZW4gdG9rZW4gYWRkcmVzcy4KVXNlZCBmb3Igc3VycGx1cyByZWNvdmVyeSAvIGFjY291bnRpbmcgcmVjb25jaWxpYXRpb24gKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAAABBBZ2dyZWdhdGVCYWxhbmNlAAAAAQAAABMAAAABAAAAOE5GVCB0b2tlbiBvd25lciwga2V5ZWQgYnkgdG9rZW4gSUQgKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAAClRva2VuT3duZXIAAAAAAAEAAAALAAAAAQAAAExORlQgc3RyZWFtIGRhdGEgbWFwcGluZyB0b2tlbiBJRCB0byBzdHJlYW0gdHlwZSBhbmQgSUQgKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAAD1Rva2VuU3RyZWFtRGF0YQAAAAABAAAACwAAAAEAAABFSW1tdXRhYmxlIHBlci1zdHJlYW0gTkZUIHRyYW5zZmVyYWJpbGl0eSBwb2xpY3kgKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAAAAAAEVRva2VuVHJhbnNmZXJhYmxlAAAAAAAAAQAAAAsAAAABAAAAOE51bWJlciBvZiBORlRzIG93bmVkIGJ5IGFuIGFkZHJlc3MgKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAACk5mdEJhbGFuY2UAAAAAAAEAAAATAAAAAQAAADtUb2tlbiBtZXRhZGF0YSwgZS5nLiwgbmFtZSwgc3ltYm9sLCBVUkkgKEluc3RhbmNlIHN0b3JhZ2UpLgAAAAANVG9rZW5NZXRhZGF0YQAAAAAAAAEAAAARAAAAAAAAACxSb3V0ZXIgY29uZmlndXJhdGlvbjogRmxvdyBjb250cmFjdCBhZGRyZXNzLgAAAAxGbG93Q29udHJhY3QAAAAAAAAALlJvdXRlciBjb25maWd1cmF0aW9uOiBMb2NrdXAgY29udHJhY3QgYWRkcmVzcy4AAAAAAA5Mb2NrdXBDb250cmFjdAAAAAAAAAAAACtSb3V0ZXIgY29uZmlndXJhdGlvbjogTkZUIGNvbnRyYWN0IGFkZHJlc3MuAAAAAAtOZnRDb250cmFjdAAAAAAAAAAAO0NvcmUtZW5naW5lIGNvbmZpZ3VyYXRpb246IHRydXN0ZWQgUm91dGVyIGNvbnRyYWN0IGFkZHJlc3MuAAAAAAZSb3V0ZXIAAAAAAAAAAAA9UGF5bWFzdGVyIGNvbmZpZ3VyYXRpb246IGxpc3Qgb2YgYWxsb3dlZCBmZWUgdG9rZW4gYWRkcmVzc2VzLgAAAAAAABBBbGxvd2VkRmVlVG9rZW5z" ]), + "AAAABAAAAC5FcnJvcnMgZW1pdHRlZCBieSB0aGUgRmxvdyBzdHJlYW1pbmcgY29udHJhY3QuAAAAAAAAAAAACUZsb3dFcnJvcgAAAAAAABwAAAAZU3RyZWFtIElEIGRvZXMgbm90IGV4aXN0LgAAAAAAAA5TdHJlYW1Ob3RGb3VuZAAAAAAAAQAAACxDYWxsZXIgaXMgbm90IGF1dGhvcml6ZWQgZm9yIHRoaXMgb3BlcmF0aW9uLgAAAAxVbmF1dGhvcml6ZWQAAAACAAAANlN0cmVhbSBpcyBwYXVzZWQ7IG9wZXJhdGlvbiByZXF1aXJlcyBhY3RpdmUgc3RyZWFtaW5nLgAAAAAADFN0cmVhbVBhdXNlZAAAAAMAAAAvU3RyZWFtIGlzIHZvaWRlZDsgbm8gZnVydGhlciBtdXRhdGlvbnMgYWxsb3dlZC4AAAAADFN0cmVhbVZvaWRlZAAAAAQAAAA3U3RyZWFtIGlzIG5vdCBwYXVzZWQ7IHJlc3RhcnQgcmVxdWlyZXMgYSBwYXVzZWQgc3RyZWFtLgAAAAAPU3RyZWFtTm90UGF1c2VkAAAAAAUAAAA5U3RyZWFtIGhhcyBub3Qgc3RhcnRlZCB5ZXQgKHNuYXBzaG90X3RpbWUgaW4gdGhlIGZ1dHVyZSkuAAAAAAAADVN0cmVhbVBlbmRpbmcAAAAAAAAGAAAAIE5ldyByYXRlIHBlciBzZWNvbmQgbXVzdCBiZSA+IDAuAAAAEVJhdGVQZXJTZWNvbmRaZXJvAAAAAAAABwAAACdOZXcgcmF0ZSBtdXN0IGRpZmZlciBmcm9tIGN1cnJlbnQgcmF0ZS4AAAAAEFJhdGVOb3REaWZmZXJlbnQAAAAIAAAAG0RlcG9zaXQgYW1vdW50IG11c3QgYmUgPiAwLgAAAAARRGVwb3NpdEFtb3VudFplcm8AAAAAAAAJAAAAHFdpdGhkcmF3IGFtb3VudCBtdXN0IGJlID4gMC4AAAASV2l0aGRyYXdBbW91bnRaZXJvAAAAAAAKAAAALVdpdGhkcmF3IGFtb3VudCBleGNlZWRzIHdpdGhkcmF3YWJsZSBiYWxhbmNlLgAAAAAAAAhPdmVyZHJhdwAAAAsAAAAaUmVmdW5kIGFtb3VudCBtdXN0IGJlID4gMC4AAAAAABBSZWZ1bmRBbW91bnRaZXJvAAAADAAAAClSZWZ1bmQgYW1vdW50IGV4Y2VlZHMgcmVmdW5kYWJsZSBiYWxhbmNlLgAAAAAAAA5SZWZ1bmRPdmVyZmxvdwAAAAAADQAAACVUb2tlbiBoYXMgPiAxOCBkZWNpbWFscywgdW5zdXBwb3J0ZWQuAAAAAAAAFEludmFsaWRUb2tlbkRlY2ltYWxzAAAADgAAADZTdHJlYW0gYmFsYW5jZSBpcyB6ZXJvIChlLmcuIHF1ZXJ5aW5nIGRlcGxldGlvbiB0aW1lKS4AAAAAAAtCYWxhbmNlWmVybwAAAAAPAAAAHUNvbnRyYWN0IGFscmVhZHkgaW5pdGlhbGl6ZWQuAAAAAAAAEkFscmVhZHlJbml0aWFsaXplZAAAAAAAEAAAAB1Db250cmFjdCBub3QgeWV0IGluaXRpYWxpemVkLgAAAAAAAA5Ob3RJbml0aWFsaXplZAAAAAAAEQAAADhDYW5ub3QgY3JlYXRlIGEgcGVuZGluZyBzdHJlYW0gd2l0aCByYXRlX3Blcl9zZWNvbmQgPSAwLgAAABdDcmVhdGVSYXRlUGVyU2Vjb25kWmVybwAAAAASAAAAOUludGVybmFsIG1hdGggZXJyb3Ig4oCUIHNob3VsZCBuZXZlciBvY2N1ciBpbiBwcm9kdWN0aW9uLgAAAAAAABJJbnZhbGlkQ2FsY3VsYXRpb24AAAAAABMAAAAxU2VuZGVyIGFuZCByZWNpcGllbnQgbXVzdCBiZSBkaWZmZXJlbnQgYWRkcmVzc2VzLgAAAAAAABVTZW5kZXJFcXVhbHNSZWNpcGllbnQAAAAAAAAUAAAAJVJhdGUgcGVyIHNlY29uZCBtdXN0IG5vdCBiZSBuZWdhdGl2ZS4AAAAAAAAMTmVnYXRpdmVSYXRlAAAAFQAAAEVUb2tlbiB0cmFuc2ZlciByZXN1bHRlZCBpbiBhIGRpZmZlcmVudCBiYWxhbmNlIGNoYW5nZSB0aGFuIHJlcXVlc3RlZC4AAAAAAAAVVG9rZW5UcmFuc2Zlck1pc21hdGNoAAAAAAAAFgAAAD9DYWxsZXItc3VwcGxpZWQgdG9rZW4gZGVjaW1hbHMgZG8gbm90IG1hdGNoIHRoZSB0b2tlbiBjb250cmFjdC4AAAAAFVRva2VuRGVjaW1hbHNNaXNtYXRjaAAAAAAAABcAAAA5Q2hlY2tlZCBhcml0aG1ldGljIG9wZXJhdGlvbiBmYWlsZWQgKG92ZXJmbG93L3VuZGVyZmxvdykuAAAAAAAAD0FyaXRobWV0aWNFcnJvcgAAAAAYAAAAH0FkbWluIHRyYW5zZmVyIGFscmVhZHkgcGVuZGluZy4AAAAAFEFkbWluVHJhbnNmZXJQZW5kaW5nAAAAGQAAACRObyBhZG1pbiB0cmFuc2ZlciBwZW5kaW5nIHRvIGFjY2VwdC4AAAAWTm9BZG1pblRyYW5zZmVyUGVuZGluZwAAAAAAGgAAADFVcGdyYWRlIGlzIHRpbWVsb2NrZWQgYW5kIGNhbm5vdCBiZSBleGVjdXRlZCB5ZXQuAAAAAAAAEVVwZ3JhZGVUaW1lbG9ja2VkAAAAAAAAGwAAAB1ObyB1cGdyYWRlIGhhcyBiZWVuIHByb3Bvc2VkLgAAAAAAABFOb1VwZ3JhZGVQcm9wb3NlZAAAAAAAABw=", + "AAAABAAAAC5FcnJvcnMgZW1pdHRlZCBieSB0aGUgTG9ja3VwIHZlc3RpbmcgY29udHJhY3QuAAAAAAAAAAAAC0xvY2t1cEVycm9yAAAAABQAAAAZU3RyZWFtIElEIGRvZXMgbm90IGV4aXN0LgAAAAAAAA5TdHJlYW1Ob3RGb3VuZAAAAAAAZQAAABlDYWxsZXIgaXMgbm90IGF1dGhvcml6ZWQuAAAAAAAADFVuYXV0aG9yaXplZAAAAGYAAAAcU3RyZWFtIGlzIGFscmVhZHkgY2FuY2VsbGVkLgAAABBBbHJlYWR5Q2FuY2VsbGVkAAAAZwAAABlTdHJlYW0gaXMgbm90IGNhbmNlbGFibGUuAAAAAAAADU5vdENhbmNlbGFibGUAAAAAAABoAAAAKVdpdGhkcmF3IGFtb3VudCBleGNlZWRzIHVubG9ja2VkIGJhbGFuY2UuAAAAAAAACE92ZXJkcmF3AAAAaQAAADxJbnZhbGlkIHRpbWUgcGFyYW1ldGVycyAoc3RhcnQgPj0gZW5kLCBjbGlmZiBvdXRzaWRlIHJhbmdlKS4AAAAQSW52YWxpZFRpbWVSYW5nZQAAAGoAAAAZVG90YWwgYW1vdW50IG11c3QgYmUgPiAwLgAAAAAAAApBbW91bnRaZXJvAAAAAABrAAAAHUNvbnRyYWN0IGFscmVhZHkgaW5pdGlhbGl6ZWQuAAAAAAAAEkFscmVhZHlJbml0aWFsaXplZAAAAAAAbAAAAB1Db250cmFjdCBub3QgeWV0IGluaXRpYWxpemVkLgAAAAAAAA5Ob3RJbml0aWFsaXplZAAAAAAAbQAAADFTZW5kZXIgYW5kIHJlY2lwaWVudCBtdXN0IGJlIGRpZmZlcmVudCBhZGRyZXNzZXMuAAAAAAAAFVNlbmRlckVxdWFsc1JlY2lwaWVudAAAAAAAAG4AAAA3c3RhcnRfdW5sb2NrX2Ftb3VudCBvciBjbGlmZl91bmxvY2tfYW1vdW50IGlzIG5lZ2F0aXZlLgAAAAAUTmVnYXRpdmVVbmxvY2tBbW91bnQAAABvAAAALkNoZWNrZWQgYWRkaXRpb24gb2YgdW5sb2NrIGFtb3VudHMgb3ZlcmZsb3dlZC4AAAAAABFVbmxvY2tTdW1PdmVyZmxvdwAAAAAAAHAAAAA3VW5sb2NrIHN1bSBpcyBub3QgaW4gdGhlIHZhbGlkIHJhbmdlIFswLCB0b3RhbF9hbW91bnRdLgAAAAAQSW52YWxpZFVubG9ja1N1bQAAAHEAAAAwRGVmZW5zaXZlIGNhbmNlbGxhdGlvbiBhbW91bnQgdmFsaWRhdGlvbiBmYWlsZWQuAAAAGUludmFsaWRDYW5jZWxsYXRpb25BbW91bnQAAAAAAAByAAAARVRva2VuIHRyYW5zZmVyIHJlc3VsdGVkIGluIGEgZGlmZmVyZW50IGJhbGFuY2UgY2hhbmdlIHRoYW4gcmVxdWVzdGVkLgAAAAAAABVUb2tlblRyYW5zZmVyTWlzbWF0Y2gAAAAAAABzAAAAOUNoZWNrZWQgYXJpdGhtZXRpYyBvcGVyYXRpb24gZmFpbGVkIChvdmVyZmxvdy91bmRlcmZsb3cpLgAAAAAAAA9Bcml0aG1ldGljRXJyb3IAAAAAdAAAAB9BZG1pbiB0cmFuc2ZlciBhbHJlYWR5IHBlbmRpbmcuAAAAABRBZG1pblRyYW5zZmVyUGVuZGluZwAAAHUAAAAkTm8gYWRtaW4gdHJhbnNmZXIgcGVuZGluZyB0byBhY2NlcHQuAAAAFk5vQWRtaW5UcmFuc2ZlclBlbmRpbmcAAAAAAHYAAAAxVXBncmFkZSBpcyB0aW1lbG9ja2VkIGFuZCBjYW5ub3QgYmUgZXhlY3V0ZWQgeWV0LgAAAAAAABFVcGdyYWRlVGltZWxvY2tlZAAAAAAAAHcAAAAdTm8gdXBncmFkZSBoYXMgYmVlbiBwcm9wb3NlZC4AAAAAAAARTm9VcGdyYWRlUHJvcG9zZWQAAAAAAAB4", + "AAAABAAAACdFcnJvcnMgc3BlY2lmaWMgdG8gdGhlIFJvdXRlciBjb250cmFjdC4AAAAAAAAAAAtSb3V0ZXJFcnJvcgAAAAAFAAAAAAAAABJBbHJlYWR5SW5pdGlhbGl6ZWQAAAAAAS0AAAAAAAAADk5vdEluaXRpYWxpemVkAAAAAAEuAAAAAAAAAA1Ob3RBdXRob3JpemVkAAAAAAABLwAAAAAAAAARSW52YWxpZFN0cmVhbVR5cGUAAAAAAAEwAAAANEEgcmVxdWlyZWQgY29udHJhY3QgYWRkcmVzcyBpcyBpbnZhbGlkICh6ZXJvL3Vuc2V0KS4AAAAWSW52YWxpZENvbnRyYWN0QWRkcmVzcwAAAAABMQ==", + "AAAAAgAAAKFTdG9yYWdlIGtleXMgZm9yIGNvbnRyYWN0IGRhdGEuCgpVc2luZyBhIHR5cGVkIGVudW0gcHJldmVudHMga2V5IGNvbGxpc2lvbnMgKFNLSUxMLm1kIMKnMykuCktleXMgYXJlIG5hbWVzcGFjZWQgYnkgdmFyaWFudCB0byBrZWVwIGRpZmZlcmVudCBkYXRhIHR5cGVzIHNlcGFyYXRlLgAAAAAAAAAAAAAHRGF0YUtleQAAAAAQAAAAAAAAACFBZG1pbiBhZGRyZXNzIChJbnN0YW5jZSBzdG9yYWdlKS4AAAAAAAAFQWRtaW4AAAAAAAAAAAAAKk5leHQgc3RyZWFtIElEIGNvdW50ZXIgKEluc3RhbmNlIHN0b3JhZ2UpLgAAAAAADE5leHRTdHJlYW1JZAAAAAEAAAA+QSBGbG93IHN0cmVhbSByZWNvcmQsIGtleWVkIGJ5IHN0cmVhbSBJRCAoUGVyc2lzdGVudCBzdG9yYWdlKS4AAAAAAApGbG93U3RyZWFtAAAAAAABAAAABgAAAAEAAABAQSBMb2NrdXAgc3RyZWFtIHJlY29yZCwga2V5ZWQgYnkgc3RyZWFtIElEIChQZXJzaXN0ZW50IHN0b3JhZ2UpLgAAAAxMb2NrdXBTdHJlYW0AAAABAAAABgAAAAEAAACTQWdncmVnYXRlIHRva2VuIGJhbGFuY2UgaGVsZCBieSB0aGUgY29udHJhY3QgZm9yIGEgZ2l2ZW4gdG9rZW4gYWRkcmVzcy4KVXNlZCBmb3Igc3VycGx1cyByZWNvdmVyeSAvIGFjY291bnRpbmcgcmVjb25jaWxpYXRpb24gKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAAABBBZ2dyZWdhdGVCYWxhbmNlAAAAAQAAABMAAAABAAAAOE5GVCB0b2tlbiBvd25lciwga2V5ZWQgYnkgdG9rZW4gSUQgKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAAClRva2VuT3duZXIAAAAAAAEAAAALAAAAAQAAAExORlQgc3RyZWFtIGRhdGEgbWFwcGluZyB0b2tlbiBJRCB0byBzdHJlYW0gdHlwZSBhbmQgSUQgKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAAD1Rva2VuU3RyZWFtRGF0YQAAAAABAAAACwAAAAEAAAA4TnVtYmVyIG9mIE5GVHMgb3duZWQgYnkgYW4gYWRkcmVzcyAoUGVyc2lzdGVudCBzdG9yYWdlKS4AAAAKTmZ0QmFsYW5jZQAAAAAAAQAAABMAAAABAAAAO1Rva2VuIG1ldGFkYXRhLCBlLmcuLCBuYW1lLCBzeW1ib2wsIFVSSSAoSW5zdGFuY2Ugc3RvcmFnZSkuAAAAAA1Ub2tlbk1ldGFkYXRhAAAAAAAAAQAAABEAAAAAAAAALFJvdXRlciBjb25maWd1cmF0aW9uOiBGbG93IGNvbnRyYWN0IGFkZHJlc3MuAAAADEZsb3dDb250cmFjdAAAAAAAAAAuUm91dGVyIGNvbmZpZ3VyYXRpb246IExvY2t1cCBjb250cmFjdCBhZGRyZXNzLgAAAAAADkxvY2t1cENvbnRyYWN0AAAAAAAAAAAAK1JvdXRlciBjb25maWd1cmF0aW9uOiBORlQgY29udHJhY3QgYWRkcmVzcy4AAAAAC05mdENvbnRyYWN0AAAAAAAAAAA9UGF5bWFzdGVyIGNvbmZpZ3VyYXRpb246IGxpc3Qgb2YgYWxsb3dlZCBmZWUgdG9rZW4gYWRkcmVzc2VzLgAAAAAAABBBbGxvd2VkRmVlVG9rZW5zAAAAAAAAAEVQZW5kaW5nIGFkbWluIGFkZHJlc3MgZm9yIHR3by1zdGVwIGFkbWluIHRyYW5zZmVyIChJbnN0YW5jZSBzdG9yYWdlKS4AAAAAAAAMUGVuZGluZ0FkbWluAAAAAQAAAC5Qcm9wb3NlZCB1cGdyYWRlIFdBU00gaGFzaCAoSW5zdGFuY2Ugc3RvcmFnZSkuAAAAAAAPUHJvcG9zZWRVcGdyYWRlAAAAAAEAAAPuAAAAIAAAAAAAAABSTGVkZ2VyIHNlcXVlbmNlIGF0IHdoaWNoIGEgcHJvcG9zZWQgdXBncmFkZSBiZWNvbWVzIGV4ZWN1dGFibGUgKEluc3RhbmNlIHN0b3JhZ2UpLgAAAAAAE1VwZ3JhZGVVbmxvY2tMZWRnZXIA" ]), options ) } @@ -753,14 +819,20 @@ export class Client extends ContractClient { set_admin: this.txFromJSON, status_of: this.txFromJSON, get_stream: this.txFromJSON, + initialize: this.txFromJSON, refund_max: this.txFromJSON, adjust_rate: this.txFromJSON, get_balance: this.txFromJSON, void_stream: this.txFromJSON, + accept_admin: this.txFromJSON, withdraw_max: this.txFromJSON, + propose_admin: this.txFromJSON, total_debt_of: this.txFromJSON, covered_debt_of: this.txFromJSON, + execute_upgrade: this.txFromJSON, + propose_upgrade: this.txFromJSON, depletion_time_of: this.txFromJSON, + extend_stream_ttl: this.txFromJSON, uncovered_debt_of: this.txFromJSON, create_and_deposit: this.txFromJSON, get_rate_per_second: this.txFromJSON, diff --git a/src/generated/lockup/src/index.ts b/src/generated/lockup/src/index.ts index 1a14c73..057efa0 100644 --- a/src/generated/lockup/src/index.ts +++ b/src/generated/lockup/src/index.ts @@ -27,7 +27,7 @@ export * as rpc from "@stellar/stellar-sdk/rpc"; /** * Core data structure for a Flow (open-ended, rate-per-second) stream. - * + * * Ported from Sablier's `Flow.Stream` struct with these adaptations: * * - `UD21x18 ratePerSecond` → `i128 rate_per_second` scaled to 18 decimals. @@ -219,37 +219,6 @@ export enum StreamStatus { } -/** - * Stable public metadata for a Stream NFT and its underlying core stream. - */ -export interface StreamMetadata { - /** - * Internal identifier assigned by the selected core engine. - */ -core_stream_id: u64; - /** - * Current holder of withdrawal and NFT-owner Flow void rights. - */ -owner: string; - /** - * Canonical cross-engine lifecycle state. - */ -status: CanonicalStreamStatus; - /** - * Core engine kind. - */ -stream_type: StreamType; - /** - * Public, deployment-scoped stream identifier. - */ -token_id: i128; - /** - * Immutable transferability policy selected when the stream is created. - */ -transferable: boolean; -} - - /** * Parameters for creating a new Lockup stream. * @@ -303,21 +272,6 @@ token: string; total_amount: i128; } -/** - * Canonical lifecycle exposed by the Router to every product surface. - * - * Core engines retain their more detailed accounting states; the Router maps - * those states into this stable cross-engine vocabulary. - */ -export enum CanonicalStreamStatus { - Pending = 0, - Active = 1, - Paused = 2, - Canceled = 3, - Completed = 4, - Failed = 5, -} - /** * Errors specific to the Stream NFT contract. */ @@ -421,9 +375,33 @@ export const FlowError = { */ 21: {message:"NegativeRate"}, /** - * The token did not debit and credit the exact requested amount. + * Token transfer resulted in a different balance change than requested. + */ + 22: {message:"TokenTransferMismatch"}, + /** + * Caller-supplied token decimals do not match the token contract. */ - 22: {message:"TokenTransferMismatch"} + 23: {message:"TokenDecimalsMismatch"}, + /** + * Checked arithmetic operation failed (overflow/underflow). + */ + 24: {message:"ArithmeticError"}, + /** + * Admin transfer already pending. + */ + 25: {message:"AdminTransferPending"}, + /** + * No admin transfer pending to accept. + */ + 26: {message:"NoAdminTransferPending"}, + /** + * Upgrade is timelocked and cannot be executed yet. + */ + 27: {message:"UpgradeTimelocked"}, + /** + * No upgrade has been proposed. + */ + 28: {message:"NoUpgradeProposed"} } /** @@ -471,13 +449,45 @@ export const LockupError = { */ 110: {message:"SenderEqualsRecipient"}, /** - * Start and cliff unlock amounts must be nonnegative and fit in total amount. + * start_unlock_amount or cliff_unlock_amount is negative. + */ + 111: {message:"NegativeUnlockAmount"}, + /** + * Checked addition of unlock amounts overflowed. + */ + 112: {message:"UnlockSumOverflow"}, + /** + * Unlock sum is not in the valid range [0, total_amount]. + */ + 113: {message:"InvalidUnlockSum"}, + /** + * Defensive cancellation amount validation failed. + */ + 114: {message:"InvalidCancellationAmount"}, + /** + * Token transfer resulted in a different balance change than requested. + */ + 115: {message:"TokenTransferMismatch"}, + /** + * Checked arithmetic operation failed (overflow/underflow). + */ + 116: {message:"ArithmeticError"}, + /** + * Admin transfer already pending. + */ + 117: {message:"AdminTransferPending"}, + /** + * No admin transfer pending to accept. + */ + 118: {message:"NoAdminTransferPending"}, + /** + * Upgrade is timelocked and cannot be executed yet. */ - 111: {message:"InvalidUnlockAmount"}, + 119: {message:"UpgradeTimelocked"}, /** - * The token did not debit and credit the exact requested amount. + * No upgrade has been proposed. */ - 112: {message:"TokenTransferMismatch"} + 120: {message:"NoUpgradeProposed"} } /** @@ -489,9 +499,9 @@ export const RouterError = { 303: {message:"NotAuthorized"}, 304: {message:"InvalidStreamType"}, /** - * Core contract addresses were already configured. + * A required contract address is invalid (zero/unset). */ - 305: {message:"AlreadyConfigured"} + 305: {message:"InvalidContractAddress"} } /** @@ -500,13 +510,13 @@ export const RouterError = { * Using a typed enum prevents key collisions (SKILL.md §3). * Keys are namespaced by variant to keep different data types separate. */ -export type DataKey = {tag: "Admin", values: void} | {tag: "NextStreamId", values: void} | {tag: "FlowStream", values: readonly [u64]} | {tag: "LockupStream", values: readonly [u64]} | {tag: "AggregateBalance", values: readonly [string]} | {tag: "TokenOwner", values: readonly [i128]} | {tag: "TokenStreamData", values: readonly [i128]} | {tag: "TokenTransferable", values: readonly [i128]} | {tag: "NftBalance", values: readonly [string]} | {tag: "TokenMetadata", values: readonly [string]} | {tag: "FlowContract", values: void} | {tag: "LockupContract", values: void} | {tag: "NftContract", values: void} | {tag: "Router", values: void} | {tag: "AllowedFeeTokens", values: void}; +export type DataKey = {tag: "Admin", values: void} | {tag: "NextStreamId", values: void} | {tag: "FlowStream", values: readonly [u64]} | {tag: "LockupStream", values: readonly [u64]} | {tag: "AggregateBalance", values: readonly [string]} | {tag: "TokenOwner", values: readonly [i128]} | {tag: "TokenStreamData", values: readonly [i128]} | {tag: "NftBalance", values: readonly [string]} | {tag: "TokenMetadata", values: readonly [string]} | {tag: "FlowContract", values: void} | {tag: "LockupContract", values: void} | {tag: "NftContract", values: void} | {tag: "AllowedFeeTokens", values: void} | {tag: "PendingAdmin", values: void} | {tag: "ProposedUpgrade", values: readonly [Buffer]} | {tag: "UpgradeUnlockLedger", values: void}; export interface Client { /** * Construct and simulate a cancel transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. * Cancel a stream and reclaim unvested tokens. - * + * * Sender-only. The stream must be cancelable and not already * depleted or canceled. Returns the refunded amount. */ @@ -541,9 +551,7 @@ export interface Client { /** * Construct and simulate a upgrade transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Upgrade the contract WASM bytecode. - * - * Admin-only. + * Emergency upgrade — bypasses timelock. */ upgrade: ({new_wasm_hash}: {new_wasm_hash: Buffer}, options?: MethodOptions) => Promise> @@ -565,9 +573,7 @@ export interface Client { /** * Construct and simulate a set_admin transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Transfer admin rights to a new address. - * - * Admin-only. + * Direct admin transfer (legacy, kept for backwards compatibility). */ set_admin: ({new_admin}: {new_admin: string}, options?: MethodOptions) => Promise> @@ -583,6 +589,21 @@ export interface Client { */ get_stream: ({stream_id}: {stream_id: u64}, options?: MethodOptions) => Promise> + /** + * Construct and simulate a initialize transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Initialize the contract with an admin address. + * + * Must be called exactly once before any other function. + * The intended admin must authorize the initialization. + */ + initialize: ({admin}: {admin: string}, options?: MethodOptions) => Promise> + + /** + * Construct and simulate a accept_admin transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Accept an admin transfer (two-step transfer, step 2). + */ + accept_admin: (options?: MethodOptions) => Promise> + /** * Construct and simulate a withdraw_max transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. * Withdraw the maximum available amount from a stream. @@ -598,21 +619,30 @@ export interface Client { is_cancelable: ({stream_id}: {stream_id: u64}, options?: MethodOptions) => Promise> /** - * Construct and simulate a configure_router transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Configure the trusted Router once. - * - * The Router-only creation path consumes a sender's pre-existing token - * allowance without requiring a nested user authorization. This is the - * call shape required by OpenZeppelin FeeForwarder sponsorship. + * Construct and simulate a propose_admin transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Propose a new admin (two-step transfer, step 1). + */ + propose_admin: ({new_admin}: {new_admin: string}, options?: MethodOptions) => Promise> + + /** + * Construct and simulate a execute_upgrade transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Execute a previously proposed upgrade after the timelock has expired. */ - configure_router: ({router}: {router: string}, options?: MethodOptions) => Promise> + execute_upgrade: ({new_wasm_hash}: {new_wasm_hash: Buffer}, options?: MethodOptions) => Promise> /** - * Construct and simulate a create_from_router transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Create a fully funded stream through the trusted Router using an - * allowance previously granted by the sender to this Lockup contract. + * Construct and simulate a propose_upgrade transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Propose a timelocked upgrade. + */ + propose_upgrade: ({new_wasm_hash}: {new_wasm_hash: Buffer}, options?: MethodOptions) => Promise> + + /** + * Construct and simulate a extend_stream_ttl transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Extend the TTL of a specific stream record. + * + * Anyone can call this to keep a long-duration stream alive. */ - create_from_router: ({params}: {params: CreateLockupParams}, options?: MethodOptions) => Promise> + extend_stream_ttl: ({stream_id}: {stream_id: u64}, options?: MethodOptions) => Promise> /** * Construct and simulate a streamed_amount_of transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. @@ -653,8 +683,6 @@ export interface Client { } export class Client extends ContractClient { static async deploy( - /** Constructor/Initialization Args for the contract's `__constructor` method */ - {admin}: {admin: string}, /** Options for initializing a Client as well as for calling a method, with extras specific to deploying. */ options: MethodOptions & Omit & { @@ -666,7 +694,7 @@ export class Client extends ContractClient { format?: "hex" | "base64"; } ): Promise> { - return ContractClient.deploy({admin}, options) + return ContractClient.deploy(null, options) } constructor(public readonly options: ContractClientOptions) { super( @@ -674,17 +702,20 @@ export class Client extends ContractClient { "AAAAAAAAASlDcmVhdGUgYSBuZXcgTG9ja3VwIHN0cmVhbS4KClRoZSBzZW5kZXIgbXVzdCBoYXZlIGFwcHJvdmVkIHRoZSB0b2tlbiB0cmFuc2ZlciBmb3IgYHBhcmFtcy50b3RhbF9hbW91bnRgLgpBbGwgdG9rZW5zIGFyZSB0cmFuc2ZlcnJlZCB0byB0aGUgY29udHJhY3QgaW1tZWRpYXRlbHkuCgojIEFyZ3VtZW50cwoqIGBwYXJhbXNgIOKAlCBBbGwgY3JlYXRpb24gcGFyYW1ldGVycyBidW5kbGVkIGludG8gYSBgQ3JlYXRlTG9ja3VwUGFyYW1zYCBzdHJ1Y3QuCgojIFJldHVybnMKVGhlIG5ld2x5IGFzc2lnbmVkIHN0cmVhbSBJRC4AAAAAAAAGY3JlYXRlAAAAAAABAAAAAAAAAAZwYXJhbXMAAAAAB9AAAAASQ3JlYXRlTG9ja3VwUGFyYW1zAAAAAAABAAAABg==", "AAAAAAAAAClDaGVjayBpZiB0aGUgc3RyZWFtIGlzIGluIGEgImNvbGQiIHN0YXRlLgAAAAAAAAdpc19jb2xkAAAAAAEAAAAAAAAACXN0cmVhbV9pZAAAAAAAAAYAAAABAAAAAQ==", "AAAAAAAAAClDaGVjayBpZiB0aGUgc3RyZWFtIGlzIGluIGEgIndhcm0iIHN0YXRlLgAAAAAAAAdpc193YXJtAAAAAAEAAAAAAAAACXN0cmVhbV9pZAAAAAAAAAYAAAABAAAAAQ==", - "AAAAAAAAADBVcGdyYWRlIHRoZSBjb250cmFjdCBXQVNNIGJ5dGVjb2RlLgoKQWRtaW4tb25seS4AAAAHdXBncmFkZQAAAAABAAAAAAAAAA1uZXdfd2FzbV9oYXNoAAAAAAAD7gAAACAAAAAA", + "AAAAAAAAAChFbWVyZ2VuY3kgdXBncmFkZSDigJQgYnlwYXNzZXMgdGltZWxvY2suAAAAB3VwZ3JhZGUAAAAAAQAAAAAAAAANbmV3X3dhc21faGFzaAAAAAAAA+4AAAAgAAAAAA==", "AAAAAAAAAHFQZXJtYW5lbnRseSByZW5vdW5jZSB0aGUgYWJpbGl0eSB0byBjYW5jZWwgYSBzdHJlYW0uCgpTZW5kZXItb25seS4gT25jZSByZW5vdW5jZWQsIHRoZSBzdHJlYW0gY2Fubm90IGJlIGNhbmNlbGVkLgAAAAAAAAhyZW5vdW5jZQAAAAIAAAAAAAAACXN0cmVhbV9pZAAAAAAAAAYAAAAAAAAABnNlbmRlcgAAAAAAEwAAAAA=", "AAAAAAAAAE5XaXRoZHJhdyB2ZXN0ZWQgdG9rZW5zIGZyb20gYSBzdHJlYW0uCgpPbmx5IHRoZSBzdHJlYW0gcmVjaXBpZW50IGNhbiB3aXRoZHJhdy4AAAAAAAh3aXRoZHJhdwAAAAQAAAAAAAAACXN0cmVhbV9pZAAAAAAAAAYAAAAAAAAABmNhbGxlcgAAAAAAEwAAAAAAAAACdG8AAAAAABMAAAAAAAAABmFtb3VudAAAAAAACwAAAAA=", - "AAAAAAAAADRUcmFuc2ZlciBhZG1pbiByaWdodHMgdG8gYSBuZXcgYWRkcmVzcy4KCkFkbWluLW9ubHkuAAAACXNldF9hZG1pbgAAAAAAAAEAAAAAAAAACW5ld19hZG1pbgAAAAAAABMAAAAA", + "AAAAAAAAAEFEaXJlY3QgYWRtaW4gdHJhbnNmZXIgKGxlZ2FjeSwga2VwdCBmb3IgYmFja3dhcmRzIGNvbXBhdGliaWxpdHkpLgAAAAAAAAlzZXRfYWRtaW4AAAAAAAABAAAAAAAAAAluZXdfYWRtaW4AAAAAAAATAAAAAA==", "AAAAAAAAACBHZXQgdGhlIHN0cmVhbSdzIGN1cnJlbnQgc3RhdHVzLgAAAAlzdGF0dXNfb2YAAAAAAAABAAAAAAAAAAlzdHJlYW1faWQAAAAAAAAGAAAAAQAAB9AAAAAMTG9ja3VwU3RhdHVz", "AAAAAAAAABtHZXQgdGhlIGZ1bGwgc3RyZWFtIHJlY29yZC4AAAAACmdldF9zdHJlYW0AAAAAAAEAAAAAAAAACXN0cmVhbV9pZAAAAAAAAAYAAAABAAAH0AAAAAxMb2NrdXBTdHJlYW0=", + "AAAAAAAAAJxJbml0aWFsaXplIHRoZSBjb250cmFjdCB3aXRoIGFuIGFkbWluIGFkZHJlc3MuCgpNdXN0IGJlIGNhbGxlZCBleGFjdGx5IG9uY2UgYmVmb3JlIGFueSBvdGhlciBmdW5jdGlvbi4KVGhlIGludGVuZGVkIGFkbWluIG11c3QgYXV0aG9yaXplIHRoZSBpbml0aWFsaXphdGlvbi4AAAAKaW5pdGlhbGl6ZQAAAAAAAQAAAAAAAAAFYWRtaW4AAAAAAAATAAAAAA==", + "AAAAAAAAADVBY2NlcHQgYW4gYWRtaW4gdHJhbnNmZXIgKHR3by1zdGVwIHRyYW5zZmVyLCBzdGVwIDIpLgAAAAAAAAxhY2NlcHRfYWRtaW4AAAAAAAAAAA==", "AAAAAAAAAHhXaXRoZHJhdyB0aGUgbWF4aW11bSBhdmFpbGFibGUgYW1vdW50IGZyb20gYSBzdHJlYW0uCgpDb252ZW5pZW5jZSBmdW5jdGlvbiDigJQgd2l0aGRyYXdzIHRoZSBlbnRpcmUgd2l0aGRyYXdhYmxlIGFtb3VudC4AAAAMd2l0aGRyYXdfbWF4AAAAAwAAAAAAAAAJc3RyZWFtX2lkAAAAAAAABgAAAAAAAAAGY2FsbGVyAAAAAAATAAAAAAAAAAJ0bwAAAAAAEwAAAAEAAAAL", - "AAAAAAAAADVBdG9taWNhbGx5IGluaXRpYWxpemUgdGhlIGNvbnRyYWN0IGR1cmluZyBkZXBsb3ltZW50LgAAAAAAAA1fX2NvbnN0cnVjdG9yAAAAAAAAAQAAAAAAAAAFYWRtaW4AAAAAAAATAAAAAA==", "AAAAAAAAACJDaGVjayBpZiB0aGUgc3RyZWFtIGlzIGNhbmNlbGFibGUuAAAAAAANaXNfY2FuY2VsYWJsZQAAAAAAAAEAAAAAAAAACXN0cmVhbV9pZAAAAAAAAAYAAAABAAAAAQ==", - "AAAAAAAAAOtDb25maWd1cmUgdGhlIHRydXN0ZWQgUm91dGVyIG9uY2UuCgpUaGUgUm91dGVyLW9ubHkgY3JlYXRpb24gcGF0aCBjb25zdW1lcyBhIHNlbmRlcidzIHByZS1leGlzdGluZyB0b2tlbgphbGxvd2FuY2Ugd2l0aG91dCByZXF1aXJpbmcgYSBuZXN0ZWQgdXNlciBhdXRob3JpemF0aW9uLiBUaGlzIGlzIHRoZQpjYWxsIHNoYXBlIHJlcXVpcmVkIGJ5IE9wZW5aZXBwZWxpbiBGZWVGb3J3YXJkZXIgc3BvbnNvcnNoaXAuAAAAABBjb25maWd1cmVfcm91dGVyAAAAAQAAAAAAAAAGcm91dGVyAAAAAAATAAAAAA==", - "AAAAAAAAAIRDcmVhdGUgYSBmdWxseSBmdW5kZWQgc3RyZWFtIHRocm91Z2ggdGhlIHRydXN0ZWQgUm91dGVyIHVzaW5nIGFuCmFsbG93YW5jZSBwcmV2aW91c2x5IGdyYW50ZWQgYnkgdGhlIHNlbmRlciB0byB0aGlzIExvY2t1cCBjb250cmFjdC4AAAASY3JlYXRlX2Zyb21fcm91dGVyAAAAAAABAAAAAAAAAAZwYXJhbXMAAAAAB9AAAAASQ3JlYXRlTG9ja3VwUGFyYW1zAAAAAAABAAAABg==", + "AAAAAAAAADBQcm9wb3NlIGEgbmV3IGFkbWluICh0d28tc3RlcCB0cmFuc2Zlciwgc3RlcCAxKS4AAAANcHJvcG9zZV9hZG1pbgAAAAAAAAEAAAAAAAAACW5ld19hZG1pbgAAAAAAABMAAAAA", + "AAAAAAAAAEVFeGVjdXRlIGEgcHJldmlvdXNseSBwcm9wb3NlZCB1cGdyYWRlIGFmdGVyIHRoZSB0aW1lbG9jayBoYXMgZXhwaXJlZC4AAAAAAAAPZXhlY3V0ZV91cGdyYWRlAAAAAAEAAAAAAAAADW5ld193YXNtX2hhc2gAAAAAAAPuAAAAIAAAAAA=", + "AAAAAAAAAB1Qcm9wb3NlIGEgdGltZWxvY2tlZCB1cGdyYWRlLgAAAAAAAA9wcm9wb3NlX3VwZ3JhZGUAAAAAAQAAAAAAAAANbmV3X3dhc21faGFzaAAAAAAAA+4AAAAgAAAAAA==", + "AAAAAAAAAGdFeHRlbmQgdGhlIFRUTCBvZiBhIHNwZWNpZmljIHN0cmVhbSByZWNvcmQuCgpBbnlvbmUgY2FuIGNhbGwgdGhpcyB0byBrZWVwIGEgbG9uZy1kdXJhdGlvbiBzdHJlYW0gYWxpdmUuAAAAABFleHRlbmRfc3RyZWFtX3R0bAAAAAAAAAEAAAAAAAAACXN0cmVhbV9pZAAAAAAAAAYAAAAA", "AAAAAAAAAD1HZXQgdGhlIHRvdGFsIHZlc3RlZCAoInN0cmVhbWVkIikgYW1vdW50IGF0IHRoZSBjdXJyZW50IHRpbWUuAAAAAAAAEnN0cmVhbWVkX2Ftb3VudF9vZgAAAAAAAQAAAAAAAAAJc3RyZWFtX2lkAAAAAAAABgAAAAEAAAAL", "AAAAAAAAABhHZXQgdGhlIHJlZnVuZGVkIGFtb3VudC4AAAATZ2V0X3JlZnVuZGVkX2Ftb3VudAAAAAABAAAAAAAAAAlzdHJlYW1faWQAAAAAAAAGAAAAAQAAAAs=", "AAAAAAAAABlHZXQgdGhlIGRlcG9zaXRlZCBhbW91bnQuAAAAAAAAFGdldF9kZXBvc2l0ZWRfYW1vdW50AAAAAQAAAAAAAAAJc3RyZWFtX2lkAAAAAAAABgAAAAEAAAAL", @@ -696,14 +727,12 @@ export class Client extends ContractClient { "AAAAAwAAAMtTdGF0dXMgb2YgYSBMb2NrdXAgc3RyZWFtLgoKVXNlcyAidGVtcGVyYXR1cmUiIHNlbWFudGljczoKLSAqKldhcm0qKiAoUGVuZGluZywgU3RyZWFtaW5nKTogdGltZSBhbG9uZSBjYW4gY2hhbmdlIHRoZSBzdGF0dXMuCi0gKipDb2xkKiogKFNldHRsZWQsIENhbmNlbGVkLCBEZXBsZXRlZCk6IHRpbWUgYWxvbmUgY2Fubm90IGNoYW5nZSB0aGUgc3RhdHVzLgAAAAAAAAAADExvY2t1cFN0YXR1cwAAAAUAAAA/Q3JlYXRlZCBidXQgc3RhcnRfdGltZSBpcyBpbiB0aGUgZnV0dXJlLiBObyB0b2tlbnMgaGF2ZSB2ZXN0ZWQuAAAAAAdQZW5kaW5nAAAAAAAAAAAyQWN0aXZlIOKAlCB0b2tlbnMgYXJlIGN1cnJlbnRseSB2ZXN0aW5nIG92ZXIgdGltZS4AAAAAAAlTdHJlYW1pbmcAAAAAAAABAAAAS0FsbCB0b2tlbnMgaGF2ZSBmdWxseSB2ZXN0ZWQuIFJlY2lwaWVudCBjYW4gd2l0aGRyYXcgdGhlIHJlbWFpbmluZyBiYWxhbmNlLgAAAAAHU2V0dGxlZAAAAAACAAAAP1NlbmRlciBjYW5jZWxlZCB0aGUgc3RyZWFtLiBVbnZlc3RlZCB0b2tlbnMgcmV0dXJuZWQgdG8gc2VuZGVyLgAAAAAIQ2FuY2VsZWQAAAADAAAAQkZ1bGx5IHdpdGhkcmF3biAoYW5kL29yIHJlZnVuZGVkKS4gTm8gdG9rZW5zIHJlbWFpbiBpbiB0aGUgc3RyZWFtLgAAAAAACERlcGxldGVkAAAABA==", "AAAAAQAAAtxDb3JlIGRhdGEgc3RydWN0dXJlIGZvciBhIExvY2t1cCAoZml4ZWQtdGVybSB2ZXN0aW5nKSBzdHJlYW0uCgpTdXBwb3J0cyBsaW5lYXIgdW5sb2NrIHdpdGggb3B0aW9uYWwgY2xpZmYuIFRoZSB2ZXN0ZWQgKCJzdHJlYW1lZCIpIGFtb3VudAphdCBhbnkgdGltZSBgdGAgaXMgY2FsY3VsYXRlZCBhczoKCmBgYHRleHQKaWYgdCA8IHN0YXJ0X3RpbWU6ICAgICAgIHZlc3RlZCA9IDAKaWYgdCA8IGNsaWZmX3RpbWU6ICAgICAgIHZlc3RlZCA9IHN0YXJ0X3VubG9ja19hbW91bnQKaWYgdCA+PSBlbmRfdGltZTogICAgICAgICB2ZXN0ZWQgPSB0b3RhbF9hbW91bnQKZWxzZToKZWxhcHNlZCA9IGZsb29yKCh0IC0gY2xpZmZfdGltZSkgLyBncmFudWxhcml0eSkgKiBncmFudWxhcml0eQpzdHJlYW1hYmxlX2R1cmF0aW9uID0gZW5kX3RpbWUgLSBjbGlmZl90aW1lCnN0cmVhbWFibGVfYW1vdW50ID0gdG90YWxfYW1vdW50IC0gc3RhcnRfdW5sb2NrX2Ftb3VudCAtIGNsaWZmX3VubG9ja19hbW91bnQKdmVzdGVkID0gc3RhcnRfdW5sb2NrX2Ftb3VudCArIGNsaWZmX3VubG9ja19hbW91bnQgKyAoZWxhcHNlZCAqIHN0cmVhbWFibGVfYW1vdW50IC8gc3RyZWFtYWJsZV9kdXJhdGlvbikKYGBgCgpUaGlzIG1pcnJvcnMgdGhlIHJlZmVyZW5jZSBsaW5lYXIgbG9ja3VwIGNhbGN1bGF0aW9uIHdpdGggZGlzY3JldGUgdW5sb2NrCnN0ZXBzIGF0IGBncmFudWxhcml0eWAtc2Vjb25kIGludGVydmFscy4AAAAAAAAADExvY2t1cFN0cmVhbQAAAA8AAABGV2hldGhlciB0aGUgc2VuZGVyIGNhbiBjYW5jZWwgdGhpcyBzdHJlYW0gYW5kIHJlY2xhaW0gdW52ZXN0ZWQgdG9rZW5zLgAAAAAACmNhbmNlbGFibGUAAAAAAAEAAABuT3B0aW9uYWwgY2xpZmYgdGltZXN0YW1wLiBObyB0b2tlbnMgYmV5b25kIGBzdGFydF91bmxvY2tfYW1vdW50YAp2ZXN0IGJlZm9yZSB0aGlzIHRpbWUuIFNldCB0byAwIGZvciBubyBjbGlmZi4AAAAAAApjbGlmZl90aW1lAAAAAAAGAAAAR0Ftb3VudCB1bmxvY2tlZCBhdCBgY2xpZmZfdGltZWAgKGluIGFkZGl0aW9uIHRvIGBzdGFydF91bmxvY2tfYW1vdW50YCkuAAAAABNjbGlmZl91bmxvY2tfYW1vdW50AAAAAAsAAAAxVW5peCB0aW1lc3RhbXAgd2hlbiB0aGUgbG9ja3VwIGlzIGZ1bGx5IHVubG9ja2VkLgAAAAAAAAhlbmRfdGltZQAAAAYAAAB9VW5sb2NrIGdyYW51bGFyaXR5IGluIHNlY29uZHMuIFRva2VucyB2ZXN0IGluIGRpc2NyZXRlIHN0ZXBzIG9mIHRoaXMKaW50ZXJ2YWwuIERlZmF1bHQgPSAxIChwZXItc2Vjb25kIHZlc3RpbmcpLiBNdXN0IGJlID4gMC4AAAAAAAALZ3JhbnVsYXJpdHkAAAAABgAAADdXaGV0aGVyIGFsbCB0b2tlbnMgaGF2ZSBiZWVuIHdpdGhkcmF3biBhbmQvb3IgcmVmdW5kZWQuAAAAAAtpc19kZXBsZXRlZAAAAAABAAAAP1RoZSBhZGRyZXNzIHRoYXQgcmVjZWl2ZXMgdG9rZW5zIGFzIHRoZXkgdW5sb2NrIChjYW4gd2l0aGRyYXcpLgAAAAAJcmVjaXBpZW50AAAAAAAAEwAAAEFBbW91bnQgcmVmdW5kZWQgdG8gc2VuZGVyIG9uIGNhbmNlbGxhdGlvbi4gWmVybyB1bmxlc3MgY2FuY2VsbGVkLgAAAAAAAA9yZWZ1bmRlZF9hbW91bnQAAAAACwAAAExUaGUgYWRkcmVzcyB0aGF0IGNyZWF0ZWQgYW5kIGZ1bmRlZCB0aGUgbG9ja3VwIChjYW4gY2FuY2VsIGlmIGBjYW5jZWxhYmxlYCkuAAAABnNlbmRlcgAAAAAAEwAAACZVbml4IHRpbWVzdGFtcCB3aGVuIHRoZSBsb2NrdXAgYmVnaW5zLgAAAAAACnN0YXJ0X3RpbWUAAAAAAAYAAAAsQW1vdW50IHVubG9ja2VkIGltbWVkaWF0ZWx5IGF0IGBzdGFydF90aW1lYC4AAAATc3RhcnRfdW5sb2NrX2Ftb3VudAAAAAALAAAAI1RoZSBTb3JvYmFuIHRva2VuIGNvbnRyYWN0IGFkZHJlc3MuAAAAAAV0b2tlbgAAAAAAABMAAAA7VG90YWwgYW1vdW50IGRlcG9zaXRlZCBpbnRvIHRoZSBzdHJlYW0gKGluIHRva2VuIGRlY2ltYWxzKS4AAAAADHRvdGFsX2Ftb3VudAAAAAsAAAAmV2hldGhlciB0aGUgc3RyZWFtIGhhcyBiZWVuIGNhbmNlbGxlZC4AAAAAAAx3YXNfY2FuY2VsZWQAAAABAAAALUN1bXVsYXRpdmUgYW1vdW50IHdpdGhkcmF3biBieSB0aGUgcmVjaXBpZW50LgAAAAAAABB3aXRoZHJhd25fYW1vdW50AAAACw==", "AAAAAwAAALFTdGF0dXMgb2YgYSBGbG93IHN0cmVhbS4KClRoZSBzdGF0dXMgaXMgZGVyaXZlZCBmcm9tIHRoZSBzdHJlYW0ncyBjdXJyZW50IHN0YXRlIChyYXRlLCBiYWxhbmNlLCBkZWJ0LCB2b2lkZWQgZmxhZykgcmF0aGVyIHRoYW4KYmVpbmcgc3RvcmVkIGRpcmVjdGx5IOKAlCBrZWVwaW5nIHN0b3JhZ2UgbWluaW1hbC4AAAAAAAAAAAAADFN0cmVhbVN0YXR1cwAAAAYAAAA+U3RyZWFtIHNjaGVkdWxlZCB0byBzdGFydCBpbiB0aGUgZnV0dXJlIChzbmFwc2hvdF90aW1lID4gbm93KS4AAAAAAAdQZW5kaW5nAAAAAAAAAAAyQWN0aXZlbHkgc3RyZWFtaW5nIHdpdGggYmFsYW5jZSBjb3ZlcmluZyBhbGwgZGVidC4AAAAAABBTdHJlYW1pbmdTb2x2ZW50AAAAAQAAADZBY3RpdmVseSBzdHJlYW1pbmcgYnV0IGRlYnQgZXhjZWVkcyBhdmFpbGFibGUgYmFsYW5jZS4AAAAAABJTdHJlYW1pbmdJbnNvbHZlbnQAAAAAAAIAAAAoUGF1c2VkIGJ5IHNlbmRlciB3aXRoIG5vIHVuY292ZXJlZCBkZWJ0LgAAAA1QYXVzZWRTb2x2ZW50AAAAAAAAAwAAACVQYXVzZWQgYnkgc2VuZGVyIHdpdGggdW5jb3ZlcmVkIGRlYnQuAAAAAAAAD1BhdXNlZEluc29sdmVudAAAAAAEAAAARVBlcm1hbmVudGx5IHN0b3BwZWQuIENhbm5vdCBiZSByZXN0YXJ0ZWQuIFVuY292ZXJlZCBkZWJ0IHdyaXR0ZW4gb2ZmLgAAAAAAAAZWb2lkZWQAAAAAAAU=", - "AAAAAQAAAEdTdGFibGUgcHVibGljIG1ldGFkYXRhIGZvciBhIFN0cmVhbSBORlQgYW5kIGl0cyB1bmRlcmx5aW5nIGNvcmUgc3RyZWFtLgAAAAAAAAAADlN0cmVhbU1ldGFkYXRhAAAAAAAGAAAAOUludGVybmFsIGlkZW50aWZpZXIgYXNzaWduZWQgYnkgdGhlIHNlbGVjdGVkIGNvcmUgZW5naW5lLgAAAAAAAA5jb3JlX3N0cmVhbV9pZAAAAAAABgAAADxDdXJyZW50IGhvbGRlciBvZiB3aXRoZHJhd2FsIGFuZCBORlQtb3duZXIgRmxvdyB2b2lkIHJpZ2h0cy4AAAAFb3duZXIAAAAAAAATAAAAJ0Nhbm9uaWNhbCBjcm9zcy1lbmdpbmUgbGlmZWN5Y2xlIHN0YXRlLgAAAAAGc3RhdHVzAAAAAAfQAAAAFUNhbm9uaWNhbFN0cmVhbVN0YXR1cwAAAAAAABFDb3JlIGVuZ2luZSBraW5kLgAAAAAAAAtzdHJlYW1fdHlwZQAAAAfQAAAAClN0cmVhbVR5cGUAAAAAACxQdWJsaWMsIGRlcGxveW1lbnQtc2NvcGVkIHN0cmVhbSBpZGVudGlmaWVyLgAAAAh0b2tlbl9pZAAAAAsAAABFSW1tdXRhYmxlIHRyYW5zZmVyYWJpbGl0eSBwb2xpY3kgc2VsZWN0ZWQgd2hlbiB0aGUgc3RyZWFtIGlzIGNyZWF0ZWQuAAAAAAAADHRyYW5zZmVyYWJsZQAAAAE=", "AAAAAQAAAINQYXJhbWV0ZXJzIGZvciBjcmVhdGluZyBhIG5ldyBMb2NrdXAgc3RyZWFtLgoKQnVuZGxlZCBpbnRvIGEgc3RydWN0IGJlY2F1c2UgU29yb2JhbiBjb250cmFjdCBmdW5jdGlvbnMKaGF2ZSBhIG1heCBvZiAxMCBwYXJhbWV0ZXJzLgAAAAAAAAAAEkNyZWF0ZUxvY2t1cFBhcmFtcwAAAAAACwAAAClXaGV0aGVyIHRoZSBzZW5kZXIgY2FuIGNhbmNlbCB0aGUgc3RyZWFtLgAAAAAAAApjYW5jZWxhYmxlAAAAAAABAAAAME9wdGlvbmFsIGNsaWZmIHRpbWVzdGFtcC4gU2V0IHRvIDAgZm9yIG5vIGNsaWZmLgAAAApjbGlmZl90aW1lAAAAAAAGAAAAKlRva2VucyB1bmxvY2tlZCBhdCBjbGlmZiAoYWRkZWQgdG8gc3RhcnQpLgAAAAAAE2NsaWZmX3VubG9ja19hbW91bnQAAAAACwAAACZVbml4IHRpbWVzdGFtcCB3aGVuIHZlc3RpbmcgY29tcGxldGVzLgAAAAAACGVuZF90aW1lAAAABgAAADFVbmxvY2sgc3RlcCBpbnRlcnZhbCBpbiBzZWNvbmRzLiAwIGRlZmF1bHRzIHRvIDEuAAAAAAAAC2dyYW51bGFyaXR5AAAAAAYAAAAgQWRkcmVzcyByZWNlaXZpbmcgdmVzdGVkIHRva2Vucy4AAAAJcmVjaXBpZW50AAAAAAAAEwAAADhBZGRyZXNzIGZ1bmRpbmcgdGhlIHN0cmVhbSAoY2FuIGNhbmNlbCBpZiBgY2FuY2VsYWJsZWApLgAAAAZzZW5kZXIAAAAAABMAAAAjVW5peCB0aW1lc3RhbXAgd2hlbiB2ZXN0aW5nIGJlZ2lucy4AAAAACnN0YXJ0X3RpbWUAAAAAAAYAAAAlVG9rZW5zIHVubG9ja2VkIGltbWVkaWF0ZWx5IGF0IHN0YXJ0LgAAAAAAABNzdGFydF91bmxvY2tfYW1vdW50AAAAAAsAAAAfU29yb2JhbiB0b2tlbiBjb250cmFjdCBhZGRyZXNzLgAAAAAFdG9rZW4AAAAAAAATAAAAKVRvdGFsIHRva2VucyB0byB2ZXN0IChpbiB0b2tlbiBkZWNpbWFscykuAAAAAAAADHRvdGFsX2Ftb3VudAAAAAs=", - "AAAAAwAAAMZDYW5vbmljYWwgbGlmZWN5Y2xlIGV4cG9zZWQgYnkgdGhlIFJvdXRlciB0byBldmVyeSBwcm9kdWN0IHN1cmZhY2UuCgpDb3JlIGVuZ2luZXMgcmV0YWluIHRoZWlyIG1vcmUgZGV0YWlsZWQgYWNjb3VudGluZyBzdGF0ZXM7IHRoZSBSb3V0ZXIgbWFwcwp0aG9zZSBzdGF0ZXMgaW50byB0aGlzIHN0YWJsZSBjcm9zcy1lbmdpbmUgdm9jYWJ1bGFyeS4AAAAAAAAAAAAVQ2Fub25pY2FsU3RyZWFtU3RhdHVzAAAAAAAABgAAAAAAAAAHUGVuZGluZwAAAAAAAAAAAAAAAAZBY3RpdmUAAAAAAAEAAAAAAAAABlBhdXNlZAAAAAAAAgAAAAAAAAAIQ2FuY2VsZWQAAAADAAAAAAAAAAlDb21wbGV0ZWQAAAAAAAAEAAAAAAAAAAZGYWlsZWQAAAAAAAU=", "AAAABAAAACtFcnJvcnMgc3BlY2lmaWMgdG8gdGhlIFN0cmVhbSBORlQgY29udHJhY3QuAAAAAAAAAAAITmZ0RXJyb3IAAAAFAAAAAAAAABJBbHJlYWR5SW5pdGlhbGl6ZWQAAAAAAMkAAAAAAAAADU5vdEF1dGhvcml6ZWQAAAAAAADKAAAAAAAAAA1Ub2tlbk5vdEZvdW5kAAAAAAAAywAAAAAAAAAPTm90VHJhbnNmZXJhYmxlAAAAAMwAAAAhVG9rZW4gSUQgaGFzIGFscmVhZHkgYmVlbiBtaW50ZWQuAAAAAAAADUFscmVhZHlNaW50ZWQAAAAAAADN", - "AAAABAAAAC5FcnJvcnMgZW1pdHRlZCBieSB0aGUgRmxvdyBzdHJlYW1pbmcgY29udHJhY3QuAAAAAAAAAAAACUZsb3dFcnJvcgAAAAAAABYAAAAZU3RyZWFtIElEIGRvZXMgbm90IGV4aXN0LgAAAAAAAA5TdHJlYW1Ob3RGb3VuZAAAAAAAAQAAACxDYWxsZXIgaXMgbm90IGF1dGhvcml6ZWQgZm9yIHRoaXMgb3BlcmF0aW9uLgAAAAxVbmF1dGhvcml6ZWQAAAACAAAANlN0cmVhbSBpcyBwYXVzZWQ7IG9wZXJhdGlvbiByZXF1aXJlcyBhY3RpdmUgc3RyZWFtaW5nLgAAAAAADFN0cmVhbVBhdXNlZAAAAAMAAAAvU3RyZWFtIGlzIHZvaWRlZDsgbm8gZnVydGhlciBtdXRhdGlvbnMgYWxsb3dlZC4AAAAADFN0cmVhbVZvaWRlZAAAAAQAAAA3U3RyZWFtIGlzIG5vdCBwYXVzZWQ7IHJlc3RhcnQgcmVxdWlyZXMgYSBwYXVzZWQgc3RyZWFtLgAAAAAPU3RyZWFtTm90UGF1c2VkAAAAAAUAAAA5U3RyZWFtIGhhcyBub3Qgc3RhcnRlZCB5ZXQgKHNuYXBzaG90X3RpbWUgaW4gdGhlIGZ1dHVyZSkuAAAAAAAADVN0cmVhbVBlbmRpbmcAAAAAAAAGAAAAIE5ldyByYXRlIHBlciBzZWNvbmQgbXVzdCBiZSA+IDAuAAAAEVJhdGVQZXJTZWNvbmRaZXJvAAAAAAAABwAAACdOZXcgcmF0ZSBtdXN0IGRpZmZlciBmcm9tIGN1cnJlbnQgcmF0ZS4AAAAAEFJhdGVOb3REaWZmZXJlbnQAAAAIAAAAG0RlcG9zaXQgYW1vdW50IG11c3QgYmUgPiAwLgAAAAARRGVwb3NpdEFtb3VudFplcm8AAAAAAAAJAAAAHFdpdGhkcmF3IGFtb3VudCBtdXN0IGJlID4gMC4AAAASV2l0aGRyYXdBbW91bnRaZXJvAAAAAAAKAAAALVdpdGhkcmF3IGFtb3VudCBleGNlZWRzIHdpdGhkcmF3YWJsZSBiYWxhbmNlLgAAAAAAAAhPdmVyZHJhdwAAAAsAAAAaUmVmdW5kIGFtb3VudCBtdXN0IGJlID4gMC4AAAAAABBSZWZ1bmRBbW91bnRaZXJvAAAADAAAAClSZWZ1bmQgYW1vdW50IGV4Y2VlZHMgcmVmdW5kYWJsZSBiYWxhbmNlLgAAAAAAAA5SZWZ1bmRPdmVyZmxvdwAAAAAADQAAACVUb2tlbiBoYXMgPiAxOCBkZWNpbWFscywgdW5zdXBwb3J0ZWQuAAAAAAAAFEludmFsaWRUb2tlbkRlY2ltYWxzAAAADgAAADZTdHJlYW0gYmFsYW5jZSBpcyB6ZXJvIChlLmcuIHF1ZXJ5aW5nIGRlcGxldGlvbiB0aW1lKS4AAAAAAAtCYWxhbmNlWmVybwAAAAAPAAAAHUNvbnRyYWN0IGFscmVhZHkgaW5pdGlhbGl6ZWQuAAAAAAAAEkFscmVhZHlJbml0aWFsaXplZAAAAAAAEAAAAB1Db250cmFjdCBub3QgeWV0IGluaXRpYWxpemVkLgAAAAAAAA5Ob3RJbml0aWFsaXplZAAAAAAAEQAAADhDYW5ub3QgY3JlYXRlIGEgcGVuZGluZyBzdHJlYW0gd2l0aCByYXRlX3Blcl9zZWNvbmQgPSAwLgAAABdDcmVhdGVSYXRlUGVyU2Vjb25kWmVybwAAAAASAAAAOUludGVybmFsIG1hdGggZXJyb3Ig4oCUIHNob3VsZCBuZXZlciBvY2N1ciBpbiBwcm9kdWN0aW9uLgAAAAAAABJJbnZhbGlkQ2FsY3VsYXRpb24AAAAAABMAAAAxU2VuZGVyIGFuZCByZWNpcGllbnQgbXVzdCBiZSBkaWZmZXJlbnQgYWRkcmVzc2VzLgAAAAAAABVTZW5kZXJFcXVhbHNSZWNpcGllbnQAAAAAAAAUAAAAJVJhdGUgcGVyIHNlY29uZCBtdXN0IG5vdCBiZSBuZWdhdGl2ZS4AAAAAAAAMTmVnYXRpdmVSYXRlAAAAFQAAAD5UaGUgdG9rZW4gZGlkIG5vdCBkZWJpdCBhbmQgY3JlZGl0IHRoZSBleGFjdCByZXF1ZXN0ZWQgYW1vdW50LgAAAAAAFVRva2VuVHJhbnNmZXJNaXNtYXRjaAAAAAAAABY=", - "AAAABAAAAC5FcnJvcnMgZW1pdHRlZCBieSB0aGUgTG9ja3VwIHZlc3RpbmcgY29udHJhY3QuAAAAAAAAAAAAC0xvY2t1cEVycm9yAAAAAAwAAAAZU3RyZWFtIElEIGRvZXMgbm90IGV4aXN0LgAAAAAAAA5TdHJlYW1Ob3RGb3VuZAAAAAAAZQAAABlDYWxsZXIgaXMgbm90IGF1dGhvcml6ZWQuAAAAAAAADFVuYXV0aG9yaXplZAAAAGYAAAAcU3RyZWFtIGlzIGFscmVhZHkgY2FuY2VsbGVkLgAAABBBbHJlYWR5Q2FuY2VsbGVkAAAAZwAAABlTdHJlYW0gaXMgbm90IGNhbmNlbGFibGUuAAAAAAAADU5vdENhbmNlbGFibGUAAAAAAABoAAAAKVdpdGhkcmF3IGFtb3VudCBleGNlZWRzIHVubG9ja2VkIGJhbGFuY2UuAAAAAAAACE92ZXJkcmF3AAAAaQAAADxJbnZhbGlkIHRpbWUgcGFyYW1ldGVycyAoc3RhcnQgPj0gZW5kLCBjbGlmZiBvdXRzaWRlIHJhbmdlKS4AAAAQSW52YWxpZFRpbWVSYW5nZQAAAGoAAAAZVG90YWwgYW1vdW50IG11c3QgYmUgPiAwLgAAAAAAAApBbW91bnRaZXJvAAAAAABrAAAAHUNvbnRyYWN0IGFscmVhZHkgaW5pdGlhbGl6ZWQuAAAAAAAAEkFscmVhZHlJbml0aWFsaXplZAAAAAAAbAAAAB1Db250cmFjdCBub3QgeWV0IGluaXRpYWxpemVkLgAAAAAAAA5Ob3RJbml0aWFsaXplZAAAAAAAbQAAADFTZW5kZXIgYW5kIHJlY2lwaWVudCBtdXN0IGJlIGRpZmZlcmVudCBhZGRyZXNzZXMuAAAAAAAAFVNlbmRlckVxdWFsc1JlY2lwaWVudAAAAAAAAG4AAABLU3RhcnQgYW5kIGNsaWZmIHVubG9jayBhbW91bnRzIG11c3QgYmUgbm9ubmVnYXRpdmUgYW5kIGZpdCBpbiB0b3RhbCBhbW91bnQuAAAAABNJbnZhbGlkVW5sb2NrQW1vdW50AAAAAG8AAAA+VGhlIHRva2VuIGRpZCBub3QgZGViaXQgYW5kIGNyZWRpdCB0aGUgZXhhY3QgcmVxdWVzdGVkIGFtb3VudC4AAAAAABVUb2tlblRyYW5zZmVyTWlzbWF0Y2gAAAAAAABw", - "AAAABAAAACdFcnJvcnMgc3BlY2lmaWMgdG8gdGhlIFJvdXRlciBjb250cmFjdC4AAAAAAAAAAAtSb3V0ZXJFcnJvcgAAAAAFAAAAAAAAABJBbHJlYWR5SW5pdGlhbGl6ZWQAAAAAAS0AAAAAAAAADk5vdEluaXRpYWxpemVkAAAAAAEuAAAAAAAAAA1Ob3RBdXRob3JpemVkAAAAAAABLwAAAAAAAAARSW52YWxpZFN0cmVhbVR5cGUAAAAAAAEwAAAAMENvcmUgY29udHJhY3QgYWRkcmVzc2VzIHdlcmUgYWxyZWFkeSBjb25maWd1cmVkLgAAABFBbHJlYWR5Q29uZmlndXJlZAAAAAAAATE=", - "AAAAAgAAAKFTdG9yYWdlIGtleXMgZm9yIGNvbnRyYWN0IGRhdGEuCgpVc2luZyBhIHR5cGVkIGVudW0gcHJldmVudHMga2V5IGNvbGxpc2lvbnMgKFNLSUxMLm1kIMKnMykuCktleXMgYXJlIG5hbWVzcGFjZWQgYnkgdmFyaWFudCB0byBrZWVwIGRpZmZlcmVudCBkYXRhIHR5cGVzIHNlcGFyYXRlLgAAAAAAAAAAAAAHRGF0YUtleQAAAAAPAAAAAAAAACFBZG1pbiBhZGRyZXNzIChJbnN0YW5jZSBzdG9yYWdlKS4AAAAAAAAFQWRtaW4AAAAAAAAAAAAAKk5leHQgc3RyZWFtIElEIGNvdW50ZXIgKEluc3RhbmNlIHN0b3JhZ2UpLgAAAAAADE5leHRTdHJlYW1JZAAAAAEAAAA+QSBGbG93IHN0cmVhbSByZWNvcmQsIGtleWVkIGJ5IHN0cmVhbSBJRCAoUGVyc2lzdGVudCBzdG9yYWdlKS4AAAAAAApGbG93U3RyZWFtAAAAAAABAAAABgAAAAEAAABAQSBMb2NrdXAgc3RyZWFtIHJlY29yZCwga2V5ZWQgYnkgc3RyZWFtIElEIChQZXJzaXN0ZW50IHN0b3JhZ2UpLgAAAAxMb2NrdXBTdHJlYW0AAAABAAAABgAAAAEAAACTQWdncmVnYXRlIHRva2VuIGJhbGFuY2UgaGVsZCBieSB0aGUgY29udHJhY3QgZm9yIGEgZ2l2ZW4gdG9rZW4gYWRkcmVzcy4KVXNlZCBmb3Igc3VycGx1cyByZWNvdmVyeSAvIGFjY291bnRpbmcgcmVjb25jaWxpYXRpb24gKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAAABBBZ2dyZWdhdGVCYWxhbmNlAAAAAQAAABMAAAABAAAAOE5GVCB0b2tlbiBvd25lciwga2V5ZWQgYnkgdG9rZW4gSUQgKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAAClRva2VuT3duZXIAAAAAAAEAAAALAAAAAQAAAExORlQgc3RyZWFtIGRhdGEgbWFwcGluZyB0b2tlbiBJRCB0byBzdHJlYW0gdHlwZSBhbmQgSUQgKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAAD1Rva2VuU3RyZWFtRGF0YQAAAAABAAAACwAAAAEAAABFSW1tdXRhYmxlIHBlci1zdHJlYW0gTkZUIHRyYW5zZmVyYWJpbGl0eSBwb2xpY3kgKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAAAAAAEVRva2VuVHJhbnNmZXJhYmxlAAAAAAAAAQAAAAsAAAABAAAAOE51bWJlciBvZiBORlRzIG93bmVkIGJ5IGFuIGFkZHJlc3MgKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAACk5mdEJhbGFuY2UAAAAAAAEAAAATAAAAAQAAADtUb2tlbiBtZXRhZGF0YSwgZS5nLiwgbmFtZSwgc3ltYm9sLCBVUkkgKEluc3RhbmNlIHN0b3JhZ2UpLgAAAAANVG9rZW5NZXRhZGF0YQAAAAAAAAEAAAARAAAAAAAAACxSb3V0ZXIgY29uZmlndXJhdGlvbjogRmxvdyBjb250cmFjdCBhZGRyZXNzLgAAAAxGbG93Q29udHJhY3QAAAAAAAAALlJvdXRlciBjb25maWd1cmF0aW9uOiBMb2NrdXAgY29udHJhY3QgYWRkcmVzcy4AAAAAAA5Mb2NrdXBDb250cmFjdAAAAAAAAAAAACtSb3V0ZXIgY29uZmlndXJhdGlvbjogTkZUIGNvbnRyYWN0IGFkZHJlc3MuAAAAAAtOZnRDb250cmFjdAAAAAAAAAAAO0NvcmUtZW5naW5lIGNvbmZpZ3VyYXRpb246IHRydXN0ZWQgUm91dGVyIGNvbnRyYWN0IGFkZHJlc3MuAAAAAAZSb3V0ZXIAAAAAAAAAAAA9UGF5bWFzdGVyIGNvbmZpZ3VyYXRpb246IGxpc3Qgb2YgYWxsb3dlZCBmZWUgdG9rZW4gYWRkcmVzc2VzLgAAAAAAABBBbGxvd2VkRmVlVG9rZW5z" ]), + "AAAABAAAAC5FcnJvcnMgZW1pdHRlZCBieSB0aGUgRmxvdyBzdHJlYW1pbmcgY29udHJhY3QuAAAAAAAAAAAACUZsb3dFcnJvcgAAAAAAABwAAAAZU3RyZWFtIElEIGRvZXMgbm90IGV4aXN0LgAAAAAAAA5TdHJlYW1Ob3RGb3VuZAAAAAAAAQAAACxDYWxsZXIgaXMgbm90IGF1dGhvcml6ZWQgZm9yIHRoaXMgb3BlcmF0aW9uLgAAAAxVbmF1dGhvcml6ZWQAAAACAAAANlN0cmVhbSBpcyBwYXVzZWQ7IG9wZXJhdGlvbiByZXF1aXJlcyBhY3RpdmUgc3RyZWFtaW5nLgAAAAAADFN0cmVhbVBhdXNlZAAAAAMAAAAvU3RyZWFtIGlzIHZvaWRlZDsgbm8gZnVydGhlciBtdXRhdGlvbnMgYWxsb3dlZC4AAAAADFN0cmVhbVZvaWRlZAAAAAQAAAA3U3RyZWFtIGlzIG5vdCBwYXVzZWQ7IHJlc3RhcnQgcmVxdWlyZXMgYSBwYXVzZWQgc3RyZWFtLgAAAAAPU3RyZWFtTm90UGF1c2VkAAAAAAUAAAA5U3RyZWFtIGhhcyBub3Qgc3RhcnRlZCB5ZXQgKHNuYXBzaG90X3RpbWUgaW4gdGhlIGZ1dHVyZSkuAAAAAAAADVN0cmVhbVBlbmRpbmcAAAAAAAAGAAAAIE5ldyByYXRlIHBlciBzZWNvbmQgbXVzdCBiZSA+IDAuAAAAEVJhdGVQZXJTZWNvbmRaZXJvAAAAAAAABwAAACdOZXcgcmF0ZSBtdXN0IGRpZmZlciBmcm9tIGN1cnJlbnQgcmF0ZS4AAAAAEFJhdGVOb3REaWZmZXJlbnQAAAAIAAAAG0RlcG9zaXQgYW1vdW50IG11c3QgYmUgPiAwLgAAAAARRGVwb3NpdEFtb3VudFplcm8AAAAAAAAJAAAAHFdpdGhkcmF3IGFtb3VudCBtdXN0IGJlID4gMC4AAAASV2l0aGRyYXdBbW91bnRaZXJvAAAAAAAKAAAALVdpdGhkcmF3IGFtb3VudCBleGNlZWRzIHdpdGhkcmF3YWJsZSBiYWxhbmNlLgAAAAAAAAhPdmVyZHJhdwAAAAsAAAAaUmVmdW5kIGFtb3VudCBtdXN0IGJlID4gMC4AAAAAABBSZWZ1bmRBbW91bnRaZXJvAAAADAAAAClSZWZ1bmQgYW1vdW50IGV4Y2VlZHMgcmVmdW5kYWJsZSBiYWxhbmNlLgAAAAAAAA5SZWZ1bmRPdmVyZmxvdwAAAAAADQAAACVUb2tlbiBoYXMgPiAxOCBkZWNpbWFscywgdW5zdXBwb3J0ZWQuAAAAAAAAFEludmFsaWRUb2tlbkRlY2ltYWxzAAAADgAAADZTdHJlYW0gYmFsYW5jZSBpcyB6ZXJvIChlLmcuIHF1ZXJ5aW5nIGRlcGxldGlvbiB0aW1lKS4AAAAAAAtCYWxhbmNlWmVybwAAAAAPAAAAHUNvbnRyYWN0IGFscmVhZHkgaW5pdGlhbGl6ZWQuAAAAAAAAEkFscmVhZHlJbml0aWFsaXplZAAAAAAAEAAAAB1Db250cmFjdCBub3QgeWV0IGluaXRpYWxpemVkLgAAAAAAAA5Ob3RJbml0aWFsaXplZAAAAAAAEQAAADhDYW5ub3QgY3JlYXRlIGEgcGVuZGluZyBzdHJlYW0gd2l0aCByYXRlX3Blcl9zZWNvbmQgPSAwLgAAABdDcmVhdGVSYXRlUGVyU2Vjb25kWmVybwAAAAASAAAAOUludGVybmFsIG1hdGggZXJyb3Ig4oCUIHNob3VsZCBuZXZlciBvY2N1ciBpbiBwcm9kdWN0aW9uLgAAAAAAABJJbnZhbGlkQ2FsY3VsYXRpb24AAAAAABMAAAAxU2VuZGVyIGFuZCByZWNpcGllbnQgbXVzdCBiZSBkaWZmZXJlbnQgYWRkcmVzc2VzLgAAAAAAABVTZW5kZXJFcXVhbHNSZWNpcGllbnQAAAAAAAAUAAAAJVJhdGUgcGVyIHNlY29uZCBtdXN0IG5vdCBiZSBuZWdhdGl2ZS4AAAAAAAAMTmVnYXRpdmVSYXRlAAAAFQAAAEVUb2tlbiB0cmFuc2ZlciByZXN1bHRlZCBpbiBhIGRpZmZlcmVudCBiYWxhbmNlIGNoYW5nZSB0aGFuIHJlcXVlc3RlZC4AAAAAAAAVVG9rZW5UcmFuc2Zlck1pc21hdGNoAAAAAAAAFgAAAD9DYWxsZXItc3VwcGxpZWQgdG9rZW4gZGVjaW1hbHMgZG8gbm90IG1hdGNoIHRoZSB0b2tlbiBjb250cmFjdC4AAAAAFVRva2VuRGVjaW1hbHNNaXNtYXRjaAAAAAAAABcAAAA5Q2hlY2tlZCBhcml0aG1ldGljIG9wZXJhdGlvbiBmYWlsZWQgKG92ZXJmbG93L3VuZGVyZmxvdykuAAAAAAAAD0FyaXRobWV0aWNFcnJvcgAAAAAYAAAAH0FkbWluIHRyYW5zZmVyIGFscmVhZHkgcGVuZGluZy4AAAAAFEFkbWluVHJhbnNmZXJQZW5kaW5nAAAAGQAAACRObyBhZG1pbiB0cmFuc2ZlciBwZW5kaW5nIHRvIGFjY2VwdC4AAAAWTm9BZG1pblRyYW5zZmVyUGVuZGluZwAAAAAAGgAAADFVcGdyYWRlIGlzIHRpbWVsb2NrZWQgYW5kIGNhbm5vdCBiZSBleGVjdXRlZCB5ZXQuAAAAAAAAEVVwZ3JhZGVUaW1lbG9ja2VkAAAAAAAAGwAAAB1ObyB1cGdyYWRlIGhhcyBiZWVuIHByb3Bvc2VkLgAAAAAAABFOb1VwZ3JhZGVQcm9wb3NlZAAAAAAAABw=", + "AAAABAAAAC5FcnJvcnMgZW1pdHRlZCBieSB0aGUgTG9ja3VwIHZlc3RpbmcgY29udHJhY3QuAAAAAAAAAAAAC0xvY2t1cEVycm9yAAAAABQAAAAZU3RyZWFtIElEIGRvZXMgbm90IGV4aXN0LgAAAAAAAA5TdHJlYW1Ob3RGb3VuZAAAAAAAZQAAABlDYWxsZXIgaXMgbm90IGF1dGhvcml6ZWQuAAAAAAAADFVuYXV0aG9yaXplZAAAAGYAAAAcU3RyZWFtIGlzIGFscmVhZHkgY2FuY2VsbGVkLgAAABBBbHJlYWR5Q2FuY2VsbGVkAAAAZwAAABlTdHJlYW0gaXMgbm90IGNhbmNlbGFibGUuAAAAAAAADU5vdENhbmNlbGFibGUAAAAAAABoAAAAKVdpdGhkcmF3IGFtb3VudCBleGNlZWRzIHVubG9ja2VkIGJhbGFuY2UuAAAAAAAACE92ZXJkcmF3AAAAaQAAADxJbnZhbGlkIHRpbWUgcGFyYW1ldGVycyAoc3RhcnQgPj0gZW5kLCBjbGlmZiBvdXRzaWRlIHJhbmdlKS4AAAAQSW52YWxpZFRpbWVSYW5nZQAAAGoAAAAZVG90YWwgYW1vdW50IG11c3QgYmUgPiAwLgAAAAAAAApBbW91bnRaZXJvAAAAAABrAAAAHUNvbnRyYWN0IGFscmVhZHkgaW5pdGlhbGl6ZWQuAAAAAAAAEkFscmVhZHlJbml0aWFsaXplZAAAAAAAbAAAAB1Db250cmFjdCBub3QgeWV0IGluaXRpYWxpemVkLgAAAAAAAA5Ob3RJbml0aWFsaXplZAAAAAAAbQAAADFTZW5kZXIgYW5kIHJlY2lwaWVudCBtdXN0IGJlIGRpZmZlcmVudCBhZGRyZXNzZXMuAAAAAAAAFVNlbmRlckVxdWFsc1JlY2lwaWVudAAAAAAAAG4AAAA3c3RhcnRfdW5sb2NrX2Ftb3VudCBvciBjbGlmZl91bmxvY2tfYW1vdW50IGlzIG5lZ2F0aXZlLgAAAAAUTmVnYXRpdmVVbmxvY2tBbW91bnQAAABvAAAALkNoZWNrZWQgYWRkaXRpb24gb2YgdW5sb2NrIGFtb3VudHMgb3ZlcmZsb3dlZC4AAAAAABFVbmxvY2tTdW1PdmVyZmxvdwAAAAAAAHAAAAA3VW5sb2NrIHN1bSBpcyBub3QgaW4gdGhlIHZhbGlkIHJhbmdlIFswLCB0b3RhbF9hbW91bnRdLgAAAAAQSW52YWxpZFVubG9ja1N1bQAAAHEAAAAwRGVmZW5zaXZlIGNhbmNlbGxhdGlvbiBhbW91bnQgdmFsaWRhdGlvbiBmYWlsZWQuAAAAGUludmFsaWRDYW5jZWxsYXRpb25BbW91bnQAAAAAAAByAAAARVRva2VuIHRyYW5zZmVyIHJlc3VsdGVkIGluIGEgZGlmZmVyZW50IGJhbGFuY2UgY2hhbmdlIHRoYW4gcmVxdWVzdGVkLgAAAAAAABVUb2tlblRyYW5zZmVyTWlzbWF0Y2gAAAAAAABzAAAAOUNoZWNrZWQgYXJpdGhtZXRpYyBvcGVyYXRpb24gZmFpbGVkIChvdmVyZmxvdy91bmRlcmZsb3cpLgAAAAAAAA9Bcml0aG1ldGljRXJyb3IAAAAAdAAAAB9BZG1pbiB0cmFuc2ZlciBhbHJlYWR5IHBlbmRpbmcuAAAAABRBZG1pblRyYW5zZmVyUGVuZGluZwAAAHUAAAAkTm8gYWRtaW4gdHJhbnNmZXIgcGVuZGluZyB0byBhY2NlcHQuAAAAFk5vQWRtaW5UcmFuc2ZlclBlbmRpbmcAAAAAAHYAAAAxVXBncmFkZSBpcyB0aW1lbG9ja2VkIGFuZCBjYW5ub3QgYmUgZXhlY3V0ZWQgeWV0LgAAAAAAABFVcGdyYWRlVGltZWxvY2tlZAAAAAAAAHcAAAAdTm8gdXBncmFkZSBoYXMgYmVlbiBwcm9wb3NlZC4AAAAAAAARTm9VcGdyYWRlUHJvcG9zZWQAAAAAAAB4", + "AAAABAAAACdFcnJvcnMgc3BlY2lmaWMgdG8gdGhlIFJvdXRlciBjb250cmFjdC4AAAAAAAAAAAtSb3V0ZXJFcnJvcgAAAAAFAAAAAAAAABJBbHJlYWR5SW5pdGlhbGl6ZWQAAAAAAS0AAAAAAAAADk5vdEluaXRpYWxpemVkAAAAAAEuAAAAAAAAAA1Ob3RBdXRob3JpemVkAAAAAAABLwAAAAAAAAARSW52YWxpZFN0cmVhbVR5cGUAAAAAAAEwAAAANEEgcmVxdWlyZWQgY29udHJhY3QgYWRkcmVzcyBpcyBpbnZhbGlkICh6ZXJvL3Vuc2V0KS4AAAAWSW52YWxpZENvbnRyYWN0QWRkcmVzcwAAAAABMQ==", + "AAAAAgAAAKFTdG9yYWdlIGtleXMgZm9yIGNvbnRyYWN0IGRhdGEuCgpVc2luZyBhIHR5cGVkIGVudW0gcHJldmVudHMga2V5IGNvbGxpc2lvbnMgKFNLSUxMLm1kIMKnMykuCktleXMgYXJlIG5hbWVzcGFjZWQgYnkgdmFyaWFudCB0byBrZWVwIGRpZmZlcmVudCBkYXRhIHR5cGVzIHNlcGFyYXRlLgAAAAAAAAAAAAAHRGF0YUtleQAAAAAQAAAAAAAAACFBZG1pbiBhZGRyZXNzIChJbnN0YW5jZSBzdG9yYWdlKS4AAAAAAAAFQWRtaW4AAAAAAAAAAAAAKk5leHQgc3RyZWFtIElEIGNvdW50ZXIgKEluc3RhbmNlIHN0b3JhZ2UpLgAAAAAADE5leHRTdHJlYW1JZAAAAAEAAAA+QSBGbG93IHN0cmVhbSByZWNvcmQsIGtleWVkIGJ5IHN0cmVhbSBJRCAoUGVyc2lzdGVudCBzdG9yYWdlKS4AAAAAAApGbG93U3RyZWFtAAAAAAABAAAABgAAAAEAAABAQSBMb2NrdXAgc3RyZWFtIHJlY29yZCwga2V5ZWQgYnkgc3RyZWFtIElEIChQZXJzaXN0ZW50IHN0b3JhZ2UpLgAAAAxMb2NrdXBTdHJlYW0AAAABAAAABgAAAAEAAACTQWdncmVnYXRlIHRva2VuIGJhbGFuY2UgaGVsZCBieSB0aGUgY29udHJhY3QgZm9yIGEgZ2l2ZW4gdG9rZW4gYWRkcmVzcy4KVXNlZCBmb3Igc3VycGx1cyByZWNvdmVyeSAvIGFjY291bnRpbmcgcmVjb25jaWxpYXRpb24gKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAAABBBZ2dyZWdhdGVCYWxhbmNlAAAAAQAAABMAAAABAAAAOE5GVCB0b2tlbiBvd25lciwga2V5ZWQgYnkgdG9rZW4gSUQgKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAAClRva2VuT3duZXIAAAAAAAEAAAALAAAAAQAAAExORlQgc3RyZWFtIGRhdGEgbWFwcGluZyB0b2tlbiBJRCB0byBzdHJlYW0gdHlwZSBhbmQgSUQgKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAAD1Rva2VuU3RyZWFtRGF0YQAAAAABAAAACwAAAAEAAAA4TnVtYmVyIG9mIE5GVHMgb3duZWQgYnkgYW4gYWRkcmVzcyAoUGVyc2lzdGVudCBzdG9yYWdlKS4AAAAKTmZ0QmFsYW5jZQAAAAAAAQAAABMAAAABAAAAO1Rva2VuIG1ldGFkYXRhLCBlLmcuLCBuYW1lLCBzeW1ib2wsIFVSSSAoSW5zdGFuY2Ugc3RvcmFnZSkuAAAAAA1Ub2tlbk1ldGFkYXRhAAAAAAAAAQAAABEAAAAAAAAALFJvdXRlciBjb25maWd1cmF0aW9uOiBGbG93IGNvbnRyYWN0IGFkZHJlc3MuAAAADEZsb3dDb250cmFjdAAAAAAAAAAuUm91dGVyIGNvbmZpZ3VyYXRpb246IExvY2t1cCBjb250cmFjdCBhZGRyZXNzLgAAAAAADkxvY2t1cENvbnRyYWN0AAAAAAAAAAAAK1JvdXRlciBjb25maWd1cmF0aW9uOiBORlQgY29udHJhY3QgYWRkcmVzcy4AAAAAC05mdENvbnRyYWN0AAAAAAAAAAA9UGF5bWFzdGVyIGNvbmZpZ3VyYXRpb246IGxpc3Qgb2YgYWxsb3dlZCBmZWUgdG9rZW4gYWRkcmVzc2VzLgAAAAAAABBBbGxvd2VkRmVlVG9rZW5zAAAAAAAAAEVQZW5kaW5nIGFkbWluIGFkZHJlc3MgZm9yIHR3by1zdGVwIGFkbWluIHRyYW5zZmVyIChJbnN0YW5jZSBzdG9yYWdlKS4AAAAAAAAMUGVuZGluZ0FkbWluAAAAAQAAAC5Qcm9wb3NlZCB1cGdyYWRlIFdBU00gaGFzaCAoSW5zdGFuY2Ugc3RvcmFnZSkuAAAAAAAPUHJvcG9zZWRVcGdyYWRlAAAAAAEAAAPuAAAAIAAAAAAAAABSTGVkZ2VyIHNlcXVlbmNlIGF0IHdoaWNoIGEgcHJvcG9zZWQgdXBncmFkZSBiZWNvbWVzIGV4ZWN1dGFibGUgKEluc3RhbmNlIHN0b3JhZ2UpLgAAAAAAE1VwZ3JhZGVVbmxvY2tMZWRnZXIA" ]), options ) } @@ -718,10 +747,14 @@ export class Client extends ContractClient { set_admin: this.txFromJSON, status_of: this.txFromJSON, get_stream: this.txFromJSON, + initialize: this.txFromJSON, + accept_admin: this.txFromJSON, withdraw_max: this.txFromJSON, is_cancelable: this.txFromJSON, - configure_router: this.txFromJSON, - create_from_router: this.txFromJSON, + propose_admin: this.txFromJSON, + execute_upgrade: this.txFromJSON, + propose_upgrade: this.txFromJSON, + extend_stream_ttl: this.txFromJSON, streamed_amount_of: this.txFromJSON, get_refunded_amount: this.txFromJSON, get_deposited_amount: this.txFromJSON, diff --git a/src/generated/paymaster/src/index.ts b/src/generated/paymaster/src/index.ts index 70e6e19..777922e 100644 --- a/src/generated/paymaster/src/index.ts +++ b/src/generated/paymaster/src/index.ts @@ -90,6 +90,9 @@ export interface Client { * * # Errors * * `PaymasterError::AlreadyInitialized` — if called more than once. + * + * # Security + * The intended admin must authorize the initialization. */ initialize: ({admin, allowed_fee_tokens}: {admin: string, allowed_fee_tokens: Array}, options?: MethodOptions) => Promise> @@ -123,6 +126,12 @@ export interface Client { * * This wraps `forward()` with a simplified interface where the relayer * address is the fee recipient and fee_amount == max_fee. + * + * # Note on Expiration + * + * This wrapper sets the expiration to `current_ledger + 1000` (~83 minutes + * at ~5 sec/ledger). This is NOT "no expiration" — it is a generous but + * finite window. For indefinite authorization, use `forward()` directly. */ collect_fee_and_invoke: ({user, fee_token, max_fee, relayer, target_contract, function_name, args}: {user: string, fee_token: string, max_fee: i128, relayer: string, target_contract: string, function_name: string, args: Array}, options?: MethodOptions) => Promise> @@ -148,11 +157,11 @@ export class Client extends ContractClient { "AAAAAAAABABBdG9taWNhbGx5IGNvbGxlY3QgYSB0b2tlbiBmZWUgZnJvbSB0aGUgdXNlciBhbmQgaW52b2tlIHRoZSB0YXJnZXQKY29udHJhY3QgZnVuY3Rpb24sIHVzaW5nIHRoZSBPcGVuWmVwcGVsaW4gZmVlIGFic3RyYWN0aW9uIGhlbHBlcnMuCgpUaGUgdXNlciBtdXN0IGF1dGhvcml6ZSBib3RoIHRoZSBmZWUgdHJhbnNmZXIgYW5kIHRoZSBkb3duc3RyZWFtCmNvbnRyYWN0IGludm9jYXRpb24gdmlhIFNvcm9iYW4ncyBuYXRpdmUgYXV0aCBmcmFtZXdvcmsuCgojIEFyZ3VtZW50cwoqIGB1c2VyYCDigJQgVGhlIGVuZCB1c2VyIHBheWluZyB0aGUgZmVlIGFuZCBhdXRob3JpemluZyB0aGUgY2FsbC4KKiBgZmVlX3Rva2VuYCDigJQgVGhlIFNBQy9TRVAtNDEgdG9rZW4gdXNlZCBmb3IgZmVlIHBheW1lbnQuCiogYGZlZV9hbW91bnRgIOKAlCBUaGUgYWN0dWFsIGZlZSBhbW91bnQgdG8gdHJhbnNmZXIgZnJvbSB1c2VyLgoqIGBtYXhfZmVlX2Ftb3VudGAg4oCUIFRoZSBtYXhpbXVtIGZlZSB0aGUgdXNlciBhdXRob3JpemVkLgoqIGBleHBpcmF0aW9uX2xlZGdlcmAg4oCUIFRoZSBsZWRnZXIgYXQgd2hpY2ggdGhlIGFwcHJvdmFsIGV4cGlyZXMuCiogYGZlZV9yZWNpcGllbnRgIOKAlCBUaGUgYWRkcmVzcyByZWNlaXZpbmcgdGhlIGZlZSAodHlwaWNhbGx5IHRoZSByZWxheWVyKS4KKiBgdGFyZ2V0X2NvbnRyYWN0YCDigJQgVGhlIGNvbnRyYWN0IHRvIGludm9rZSAoZS5nLiBSb3V0ZXIpLgoqIGBmdW5jdGlvbl9uYW1lYCDigJQgVGhlIGZ1bmN0aW9uIHRvIGNhbGwgb24gdGhlIHRhcmdldCBjb250cmFjdC4KKiBgYXJnc2Ag4oCUIEFyZ3VtZW50cyB0byBwYXNzIHRvIHRoZSB0YXJnZXQgZnVuY3Rpb24uCgojIFJldHVybnMKVGhlIHJhdyBgVmFsYCByZXN1bHQgZnJvbSB0aGUgdGFyZ2V0IGNvbnRyYWN0IGludm9jYXRpb24uCgojIEVycm9ycwoqIGBQYXltYXN0ZXJFcnJvcjo6Tm90SW5pdGlhbGl6ZWRgIOKAlCBjb250cmFjdCBub3QgaW5pdGlhbGl6ZWQuCiogYFBheW1hc3RlckVycm9yOjpUb2tlbk5vdEFsAAAAB2ZvcndhcmQAAAAACQAAAAAAAAAEdXNlcgAAABMAAAAAAAAACWZlZV90b2tlbgAAAAAAABMAAAAAAAAACmZlZV9hbW91bnQAAAAAAAsAAAAAAAAADm1heF9mZWVfYW1vdW50AAAAAAALAAAAAAAAABFleHBpcmF0aW9uX2xlZGdlcgAAAAAAAAQAAAAAAAAADWZlZV9yZWNpcGllbnQAAAAAAAATAAAAAAAAAA90YXJnZXRfY29udHJhY3QAAAAAEwAAAAAAAAANZnVuY3Rpb25fbmFtZQAAAAAAABEAAAAAAAAABGFyZ3MAAAPqAAAAAAAAAAEAAAAA", "AAAAAAAAAElVcGdyYWRlIHRoZSBjb250cmFjdCBXQVNNIGJ5dGVjb2RlLgoKIyBBdXRoClJlcXVpcmVzIGFkbWluIGF1dGhvcml6YXRpb24uAAAAAAAAB3VwZ3JhZGUAAAAAAQAAAAAAAAANbmV3X3dhc21faGFzaAAAAAAAA+4AAAAgAAAAAA==", "AAAAAAAAABpSZXR1cm5zIHRoZSBhZG1pbiBhZGRyZXNzLgAAAAAACWdldF9hZG1pbgAAAAAAAAAAAAABAAAAEw==", - "AAAAAAAAAUxJbml0aWFsaXplIHRoZSBQYXltYXN0ZXIgd2l0aCBhbiBhZG1pbiBhbmQgYW4gaW5pdGlhbCBsaXN0IG9mCmFsbG93ZWQgZmVlIHRva2Vucy4KCiMgQXJndW1lbnRzCiogYGFkbWluYCDigJQgVGhlIGFkbWluIGFkZHJlc3MgKGNhbiB1cGdyYWRlIGFuZCBtYW5hZ2UgdG9rZW4gd2hpdGVsaXN0KS4KKiBgYWxsb3dlZF9mZWVfdG9rZW5zYCDigJQgSW5pdGlhbCBsaXN0IG9mIHRva2VuIGFkZHJlc3NlcyBhY2NlcHRlZApmb3IgZmVlIHBheW1lbnQuCgojIEVycm9ycwoqIGBQYXltYXN0ZXJFcnJvcjo6QWxyZWFkeUluaXRpYWxpemVkYCDigJQgaWYgY2FsbGVkIG1vcmUgdGhhbiBvbmNlLgAAAAppbml0aWFsaXplAAAAAAACAAAAAAAAAAVhZG1pbgAAAAAAABMAAAAAAAAAEmFsbG93ZWRfZmVlX3Rva2VucwAAAAAD6gAAABMAAAAA", + "AAAAAAAAAY5Jbml0aWFsaXplIHRoZSBQYXltYXN0ZXIgd2l0aCBhbiBhZG1pbiBhbmQgYW4gaW5pdGlhbCBsaXN0IG9mCmFsbG93ZWQgZmVlIHRva2Vucy4KCiMgQXJndW1lbnRzCiogYGFkbWluYCDigJQgVGhlIGFkbWluIGFkZHJlc3MgKGNhbiB1cGdyYWRlIGFuZCBtYW5hZ2UgdG9rZW4gd2hpdGVsaXN0KS4KKiBgYWxsb3dlZF9mZWVfdG9rZW5zYCDigJQgSW5pdGlhbCBsaXN0IG9mIHRva2VuIGFkZHJlc3NlcyBhY2NlcHRlZApmb3IgZmVlIHBheW1lbnQuCgojIEVycm9ycwoqIGBQYXltYXN0ZXJFcnJvcjo6QWxyZWFkeUluaXRpYWxpemVkYCDigJQgaWYgY2FsbGVkIG1vcmUgdGhhbiBvbmNlLgoKIyBTZWN1cml0eQpUaGUgaW50ZW5kZWQgYWRtaW4gbXVzdCBhdXRob3JpemUgdGhlIGluaXRpYWxpemF0aW9uLgAAAAAACmluaXRpYWxpemUAAAAAAAIAAAAAAAAABWFkbWluAAAAAAAAEwAAAAAAAAASYWxsb3dlZF9mZWVfdG9rZW5zAAAAAAPqAAAAEwAAAAA=", "AAAAAAAAAFVBZGQgYSB0b2tlbiB0byB0aGUgYWxsb3dlZCBmZWUgdG9rZW4gd2hpdGVsaXN0LgoKIyBBdXRoClJlcXVpcmVzIGFkbWluIGF1dGhvcml6YXRpb24uAAAAAAAADWFkZF9mZWVfdG9rZW4AAAAAAAABAAAAAAAAAAV0b2tlbgAAAAAAABMAAAAA", "AAAAAAAAAFpSZW1vdmUgYSB0b2tlbiBmcm9tIHRoZSBhbGxvd2VkIGZlZSB0b2tlbiB3aGl0ZWxpc3QuCgojIEF1dGgKUmVxdWlyZXMgYWRtaW4gYXV0aG9yaXphdGlvbi4AAAAAABByZW1vdmVfZmVlX3Rva2VuAAAAAQAAAAAAAAAFdG9rZW4AAAAAAAATAAAAAA==", "AAAAAAAAADtDaGVjayBpZiBhIHNwZWNpZmljIHRva2VuIGlzIGluIHRoZSBhbGxvd2VkIGZlZSB0b2tlbiBsaXN0LgAAAAAUaXNfZmVlX3Rva2VuX2FsbG93ZWQAAAABAAAAAAAAAAV0b2tlbgAAAAAAABMAAAABAAAAAQ==", - "AAAAAAAAAMNCYWNrd2FyZHMtY29tcGF0aWJsZSBlbnRyeSBwb2ludCBtYXRjaGluZyB0aGUgb3JpZ2luYWwgUGF5bWFzdGVyIEFQSS4KClRoaXMgd3JhcHMgYGZvcndhcmQoKWAgd2l0aCBhIHNpbXBsaWZpZWQgaW50ZXJmYWNlIHdoZXJlIHRoZSByZWxheWVyCmFkZHJlc3MgaXMgdGhlIGZlZSByZWNpcGllbnQgYW5kIGZlZV9hbW91bnQgPT0gbWF4X2ZlZS4AAAAAFmNvbGxlY3RfZmVlX2FuZF9pbnZva2UAAAAAAAcAAAAAAAAABHVzZXIAAAATAAAAAAAAAAlmZWVfdG9rZW4AAAAAAAATAAAAAAAAAAdtYXhfZmVlAAAAAAsAAAAAAAAAB3JlbGF5ZXIAAAAAEwAAAAAAAAAPdGFyZ2V0X2NvbnRyYWN0AAAAABMAAAAAAAAADWZ1bmN0aW9uX25hbWUAAAAAAAARAAAAAAAAAARhcmdzAAAD6gAAAAAAAAABAAAAAA==", + "AAAAAAAAAbJCYWNrd2FyZHMtY29tcGF0aWJsZSBlbnRyeSBwb2ludCBtYXRjaGluZyB0aGUgb3JpZ2luYWwgUGF5bWFzdGVyIEFQSS4KClRoaXMgd3JhcHMgYGZvcndhcmQoKWAgd2l0aCBhIHNpbXBsaWZpZWQgaW50ZXJmYWNlIHdoZXJlIHRoZSByZWxheWVyCmFkZHJlc3MgaXMgdGhlIGZlZSByZWNpcGllbnQgYW5kIGZlZV9hbW91bnQgPT0gbWF4X2ZlZS4KCiMgTm90ZSBvbiBFeHBpcmF0aW9uCgpUaGlzIHdyYXBwZXIgc2V0cyB0aGUgZXhwaXJhdGlvbiB0byBgY3VycmVudF9sZWRnZXIgKyAxMDAwYCAofjgzIG1pbnV0ZXMKYXQgfjUgc2VjL2xlZGdlcikuIFRoaXMgaXMgTk9UICJubyBleHBpcmF0aW9uIiDigJQgaXQgaXMgYSBnZW5lcm91cyBidXQKZmluaXRlIHdpbmRvdy4gRm9yIGluZGVmaW5pdGUgYXV0aG9yaXphdGlvbiwgdXNlIGBmb3J3YXJkKClgIGRpcmVjdGx5LgAAAAAAFmNvbGxlY3RfZmVlX2FuZF9pbnZva2UAAAAAAAcAAAAAAAAABHVzZXIAAAATAAAAAAAAAAlmZWVfdG9rZW4AAAAAAAATAAAAAAAAAAdtYXhfZmVlAAAAAAsAAAAAAAAAB3JlbGF5ZXIAAAAAEwAAAAAAAAAPdGFyZ2V0X2NvbnRyYWN0AAAAABMAAAAAAAAADWZ1bmN0aW9uX25hbWUAAAAAAAARAAAAAAAAAARhcmdzAAAD6gAAAAAAAAABAAAAAA==", "AAAABQAAADZFdmVudCBlbWl0dGVkIHdoZW4gdG9rZW5zIGFyZSBzd2VwdCBmcm9tIHRoZSBjb250cmFjdC4AAAAAAAAAAAALVG9rZW5zU3dlcHQAAAAAAQAAAAx0b2tlbnNfc3dlcHQAAAADAAAAAAAAAAV0b2tlbgAAAAAAABMAAAABAAAAAAAAAAlyZWNpcGllbnQAAAAAAAATAAAAAQAAAAAAAAAGYW1vdW50AAAAAAALAAAAAAAAAAI=", "AAAABQAAADJFdmVudCBlbWl0dGVkIHdoZW4gYSBmZWUgaXMgY29sbGVjdGVkIGZyb20gYSB1c2VyLgAAAAAAAAAAAAxGZWVDb2xsZWN0ZWQAAAABAAAADWZlZV9jb2xsZWN0ZWQAAAAAAAAEAAAAAAAAAAR1c2VyAAAAEwAAAAEAAAAAAAAACXJlY2lwaWVudAAAAAAAABMAAAABAAAAAAAAAAV0b2tlbgAAAAAAABMAAAAAAAAAAAAAAAZhbW91bnQAAAAAAAsAAAAAAAAAAg==", "AAAABQAAADxFdmVudCBlbWl0dGVkIHdoZW4gYSBjYWxsIGlzIGZvcndhcmRlZCB0byBhIHRhcmdldCBjb250cmFjdC4AAAAAAAAAD0ZvcndhcmRFeGVjdXRlZAAAAAABAAAAEGZvcndhcmRfZXhlY3V0ZWQAAAAEAAAAAAAAAAR1c2VyAAAAEwAAAAEAAAAAAAAAD3RhcmdldF9jb250cmFjdAAAAAATAAAAAQAAAAAAAAAJdGFyZ2V0X2ZuAAAAAAAAEQAAAAAAAAAAAAAAC3RhcmdldF9hcmdzAAAAA+oAAAAAAAAAAAAAAAI=", diff --git a/src/generated/router/src/index.ts b/src/generated/router/src/index.ts index 8ac9817..c6a01ba 100644 --- a/src/generated/router/src/index.ts +++ b/src/generated/router/src/index.ts @@ -27,7 +27,7 @@ export * as rpc from "@stellar/stellar-sdk/rpc"; /** * Core data structure for a Flow (open-ended, rate-per-second) stream. - * + * * Ported from Sablier's `Flow.Stream` struct with these adaptations: * * - `UD21x18 ratePerSecond` → `i128 rate_per_second` scaled to 18 decimals. @@ -219,37 +219,6 @@ export enum StreamStatus { } -/** - * Stable public metadata for a Stream NFT and its underlying core stream. - */ -export interface StreamMetadata { - /** - * Internal identifier assigned by the selected core engine. - */ -core_stream_id: u64; - /** - * Current holder of withdrawal and NFT-owner Flow void rights. - */ -owner: string; - /** - * Canonical cross-engine lifecycle state. - */ -status: CanonicalStreamStatus; - /** - * Core engine kind. - */ -stream_type: StreamType; - /** - * Public, deployment-scoped stream identifier. - */ -token_id: i128; - /** - * Immutable transferability policy selected when the stream is created. - */ -transferable: boolean; -} - - /** * Parameters for creating a new Lockup stream. * @@ -303,21 +272,6 @@ token: string; total_amount: i128; } -/** - * Canonical lifecycle exposed by the Router to every product surface. - * - * Core engines retain their more detailed accounting states; the Router maps - * those states into this stable cross-engine vocabulary. - */ -export enum CanonicalStreamStatus { - Pending = 0, - Active = 1, - Paused = 2, - Canceled = 3, - Completed = 4, - Failed = 5, -} - /** * Errors specific to the Stream NFT contract. */ @@ -421,9 +375,33 @@ export const FlowError = { */ 21: {message:"NegativeRate"}, /** - * The token did not debit and credit the exact requested amount. + * Token transfer resulted in a different balance change than requested. + */ + 22: {message:"TokenTransferMismatch"}, + /** + * Caller-supplied token decimals do not match the token contract. + */ + 23: {message:"TokenDecimalsMismatch"}, + /** + * Checked arithmetic operation failed (overflow/underflow). */ - 22: {message:"TokenTransferMismatch"} + 24: {message:"ArithmeticError"}, + /** + * Admin transfer already pending. + */ + 25: {message:"AdminTransferPending"}, + /** + * No admin transfer pending to accept. + */ + 26: {message:"NoAdminTransferPending"}, + /** + * Upgrade is timelocked and cannot be executed yet. + */ + 27: {message:"UpgradeTimelocked"}, + /** + * No upgrade has been proposed. + */ + 28: {message:"NoUpgradeProposed"} } /** @@ -471,13 +449,45 @@ export const LockupError = { */ 110: {message:"SenderEqualsRecipient"}, /** - * Start and cliff unlock amounts must be nonnegative and fit in total amount. + * start_unlock_amount or cliff_unlock_amount is negative. */ - 111: {message:"InvalidUnlockAmount"}, + 111: {message:"NegativeUnlockAmount"}, /** - * The token did not debit and credit the exact requested amount. + * Checked addition of unlock amounts overflowed. */ - 112: {message:"TokenTransferMismatch"} + 112: {message:"UnlockSumOverflow"}, + /** + * Unlock sum is not in the valid range [0, total_amount]. + */ + 113: {message:"InvalidUnlockSum"}, + /** + * Defensive cancellation amount validation failed. + */ + 114: {message:"InvalidCancellationAmount"}, + /** + * Token transfer resulted in a different balance change than requested. + */ + 115: {message:"TokenTransferMismatch"}, + /** + * Checked arithmetic operation failed (overflow/underflow). + */ + 116: {message:"ArithmeticError"}, + /** + * Admin transfer already pending. + */ + 117: {message:"AdminTransferPending"}, + /** + * No admin transfer pending to accept. + */ + 118: {message:"NoAdminTransferPending"}, + /** + * Upgrade is timelocked and cannot be executed yet. + */ + 119: {message:"UpgradeTimelocked"}, + /** + * No upgrade has been proposed. + */ + 120: {message:"NoUpgradeProposed"} } /** @@ -489,9 +499,9 @@ export const RouterError = { 303: {message:"NotAuthorized"}, 304: {message:"InvalidStreamType"}, /** - * Core contract addresses were already configured. + * A required contract address is invalid (zero/unset). */ - 305: {message:"AlreadyConfigured"} + 305: {message:"InvalidContractAddress"} } /** @@ -500,7 +510,7 @@ export const RouterError = { * Using a typed enum prevents key collisions (SKILL.md §3). * Keys are namespaced by variant to keep different data types separate. */ -export type DataKey = {tag: "Admin", values: void} | {tag: "NextStreamId", values: void} | {tag: "FlowStream", values: readonly [u64]} | {tag: "LockupStream", values: readonly [u64]} | {tag: "AggregateBalance", values: readonly [string]} | {tag: "TokenOwner", values: readonly [i128]} | {tag: "TokenStreamData", values: readonly [i128]} | {tag: "TokenTransferable", values: readonly [i128]} | {tag: "NftBalance", values: readonly [string]} | {tag: "TokenMetadata", values: readonly [string]} | {tag: "FlowContract", values: void} | {tag: "LockupContract", values: void} | {tag: "NftContract", values: void} | {tag: "Router", values: void} | {tag: "AllowedFeeTokens", values: void}; +export type DataKey = {tag: "Admin", values: void} | {tag: "NextStreamId", values: void} | {tag: "FlowStream", values: readonly [u64]} | {tag: "LockupStream", values: readonly [u64]} | {tag: "AggregateBalance", values: readonly [string]} | {tag: "TokenOwner", values: readonly [i128]} | {tag: "TokenStreamData", values: readonly [i128]} | {tag: "NftBalance", values: readonly [string]} | {tag: "TokenMetadata", values: readonly [string]} | {tag: "FlowContract", values: void} | {tag: "LockupContract", values: void} | {tag: "NftContract", values: void} | {tag: "AllowedFeeTokens", values: void} | {tag: "PendingAdmin", values: void} | {tag: "ProposedUpgrade", values: readonly [Buffer]} | {tag: "UpgradeUnlockLedger", values: void}; export interface Client { /** @@ -509,12 +519,6 @@ export interface Client { */ upgrade: ({new_wasm_hash}: {new_wasm_hash: Buffer}, options?: MethodOptions) => Promise> - /** - * Construct and simulate a owner_of transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Return the current NFT owner for a public stream ID. - */ - owner_of: ({token_id}: {token_id: i128}, options?: MethodOptions) => Promise> - /** * Construct and simulate a withdraw transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. * Withdraw tokens from a stream using the NFT. @@ -522,76 +526,65 @@ export interface Client { withdraw: ({token_id, caller, to, amount}: {token_id: i128, caller: string, to: string, amount: i128}, options?: MethodOptions) => Promise> /** - * Construct and simulate a configure transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Configure core contract addresses once after all contracts are deployed. + * Construct and simulate a initialize transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Initialize the Router with the addresses of the core contracts. + * + * The intended admin must authorize the initialization to prevent + * first-caller takeover attacks. */ - configure: ({flow_contract, lockup_contract, nft_contract}: {flow_contract: string, lockup_contract: string, nft_contract: string}, options?: MethodOptions) => Promise> + initialize: ({admin, flow_contract, lockup_contract, nft_contract}: {admin: string, flow_contract: string, lockup_contract: string, nft_contract: string}, options?: MethodOptions) => Promise> /** - * Construct and simulate a set_admin transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Transfer Router administration to a new governance address. - */ - set_admin: ({new_admin}: {new_admin: string}, options?: MethodOptions) => Promise> - - /** - * Construct and simulate a status_of transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Return the canonical cross-engine lifecycle for a public stream ID. - */ - status_of: ({token_id}: {token_id: i128}, options?: MethodOptions) => Promise> - - /** - * Construct and simulate a void_flow transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Permanently void a Flow stream as its current NFT owner. + * Construct and simulate a upgrade_nft transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Admin can upgrade the NFT contract logic. */ - void_flow: ({token_id, caller}: {token_id: i128, caller: string}, options?: MethodOptions) => Promise> + upgrade_nft: ({new_wasm_hash}: {new_wasm_hash: Buffer}, options?: MethodOptions) => Promise> /** - * Construct and simulate a get_stream transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Return stable public metadata and the canonical lifecycle in one query. + * Construct and simulate a accept_admin transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Accept an admin transfer. */ - get_stream: ({token_id}: {token_id: i128}, options?: MethodOptions) => Promise> + accept_admin: (options?: MethodOptions) => Promise> /** - * Construct and simulate a stream_type transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Return the core engine kind for a public stream ID. + * Construct and simulate a withdraw_max transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Withdraw max tokens from a stream using the NFT. */ - stream_type: ({token_id}: {token_id: i128}, options?: MethodOptions) => Promise> + withdraw_max: ({token_id, caller, to}: {token_id: i128, caller: string, to: string}, options?: MethodOptions) => Promise> /** - * Construct and simulate a upgrade_nft transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Admin can upgrade the NFT contract logic. + * Construct and simulate a propose_admin transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Propose a new admin (two-step transfer). */ - upgrade_nft: ({new_wasm_hash}: {new_wasm_hash: Buffer}, options?: MethodOptions) => Promise> + propose_admin: ({new_admin}: {new_admin: string}, options?: MethodOptions) => Promise> /** - * Construct and simulate a withdraw_max transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Withdraw max tokens from a stream using the NFT. + * Construct and simulate a execute_upgrade transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Execute a previously proposed upgrade after the timelock. */ - withdraw_max: ({token_id, caller, to}: {token_id: i128, caller: string, to: string}, options?: MethodOptions) => Promise> + execute_upgrade: ({new_wasm_hash}: {new_wasm_hash: Buffer}, options?: MethodOptions) => Promise> /** - * Construct and simulate a core_stream_id transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Return the internal core engine ID for a public stream ID. + * Construct and simulate a propose_upgrade transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Propose a timelocked upgrade. */ - core_stream_id: ({token_id}: {token_id: i128}, options?: MethodOptions) => Promise> + propose_upgrade: ({new_wasm_hash}: {new_wasm_hash: Buffer}, options?: MethodOptions) => Promise> /** * Construct and simulate a create_flow_stream transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. * Create a Flow stream and mint an NFT. */ - create_flow_stream: ({sender, recipient, token, rate_per_second, token_decimals, start_time, initial_amount, transferable}: {sender: string, recipient: string, token: string, rate_per_second: i128, token_decimals: u32, start_time: u64, initial_amount: i128, transferable: boolean}, options?: MethodOptions) => Promise> + create_flow_stream: ({sender, recipient, token, rate_per_second, token_decimals, start_time}: {sender: string, recipient: string, token: string, rate_per_second: i128, token_decimals: u32, start_time: u64}, options?: MethodOptions) => Promise> /** * Construct and simulate a create_lockup_stream transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. * Create a Lockup stream and mint an NFT. */ - create_lockup_stream: ({params, transferable}: {params: CreateLockupParams, transferable: boolean}, options?: MethodOptions) => Promise> + create_lockup_stream: ({params}: {params: CreateLockupParams}, options?: MethodOptions) => Promise> } export class Client extends ContractClient { static async deploy( - /** Constructor/Initialization Args for the contract's `__constructor` method */ - {admin}: {admin: string}, /** Options for initializing a Client as well as for calling a method, with extras specific to deploying. */ options: MethodOptions & Omit & { @@ -603,54 +596,45 @@ export class Client extends ContractClient { format?: "hex" | "base64"; } ): Promise> { - return ContractClient.deploy({admin}, options) + return ContractClient.deploy(null, options) } constructor(public readonly options: ContractClientOptions) { super( new ContractSpec([ "AAAAAAAAACNBZG1pbiBjYW4gdXBncmFkZSB0aGUgcm91dGVyIGxvZ2ljLgAAAAAHdXBncmFkZQAAAAABAAAAAAAAAA1uZXdfd2FzbV9oYXNoAAAAAAAD7gAAACAAAAAA", - "AAAAAAAAADRSZXR1cm4gdGhlIGN1cnJlbnQgTkZUIG93bmVyIGZvciBhIHB1YmxpYyBzdHJlYW0gSUQuAAAACG93bmVyX29mAAAAAQAAAAAAAAAIdG9rZW5faWQAAAALAAAAAQAAABM=", "AAAAAAAAACxXaXRoZHJhdyB0b2tlbnMgZnJvbSBhIHN0cmVhbSB1c2luZyB0aGUgTkZULgAAAAh3aXRoZHJhdwAAAAQAAAAAAAAACHRva2VuX2lkAAAACwAAAAAAAAAGY2FsbGVyAAAAAAATAAAAAAAAAAJ0bwAAAAAAEwAAAAAAAAAGYW1vdW50AAAAAAALAAAAAA==", - "AAAAAAAAAEhDb25maWd1cmUgY29yZSBjb250cmFjdCBhZGRyZXNzZXMgb25jZSBhZnRlciBhbGwgY29udHJhY3RzIGFyZSBkZXBsb3llZC4AAAAJY29uZmlndXJlAAAAAAAAAwAAAAAAAAANZmxvd19jb250cmFjdAAAAAAAABMAAAAAAAAAD2xvY2t1cF9jb250cmFjdAAAAAATAAAAAAAAAAxuZnRfY29udHJhY3QAAAATAAAAAA==", - "AAAAAAAAADtUcmFuc2ZlciBSb3V0ZXIgYWRtaW5pc3RyYXRpb24gdG8gYSBuZXcgZ292ZXJuYW5jZSBhZGRyZXNzLgAAAAAJc2V0X2FkbWluAAAAAAAAAQAAAAAAAAAJbmV3X2FkbWluAAAAAAAAEwAAAAA=", - "AAAAAAAAAENSZXR1cm4gdGhlIGNhbm9uaWNhbCBjcm9zcy1lbmdpbmUgbGlmZWN5Y2xlIGZvciBhIHB1YmxpYyBzdHJlYW0gSUQuAAAAAAlzdGF0dXNfb2YAAAAAAAABAAAAAAAAAAh0b2tlbl9pZAAAAAsAAAABAAAH0AAAABVDYW5vbmljYWxTdHJlYW1TdGF0dXMAAAA=", - "AAAAAAAAADhQZXJtYW5lbnRseSB2b2lkIGEgRmxvdyBzdHJlYW0gYXMgaXRzIGN1cnJlbnQgTkZUIG93bmVyLgAAAAl2b2lkX2Zsb3cAAAAAAAACAAAAAAAAAAh0b2tlbl9pZAAAAAsAAAAAAAAABmNhbGxlcgAAAAAAEwAAAAA=", - "AAAAAAAAAEdSZXR1cm4gc3RhYmxlIHB1YmxpYyBtZXRhZGF0YSBhbmQgdGhlIGNhbm9uaWNhbCBsaWZlY3ljbGUgaW4gb25lIHF1ZXJ5LgAAAAAKZ2V0X3N0cmVhbQAAAAAAAQAAAAAAAAAIdG9rZW5faWQAAAALAAAAAQAAB9AAAAAOU3RyZWFtTWV0YWRhdGEAAA==", - "AAAAAAAAADNSZXR1cm4gdGhlIGNvcmUgZW5naW5lIGtpbmQgZm9yIGEgcHVibGljIHN0cmVhbSBJRC4AAAAAC3N0cmVhbV90eXBlAAAAAAEAAAAAAAAACHRva2VuX2lkAAAACwAAAAEAAAfQAAAAClN0cmVhbVR5cGUAAA==", + "AAAAAAAAAJ9Jbml0aWFsaXplIHRoZSBSb3V0ZXIgd2l0aCB0aGUgYWRkcmVzc2VzIG9mIHRoZSBjb3JlIGNvbnRyYWN0cy4KClRoZSBpbnRlbmRlZCBhZG1pbiBtdXN0IGF1dGhvcml6ZSB0aGUgaW5pdGlhbGl6YXRpb24gdG8gcHJldmVudApmaXJzdC1jYWxsZXIgdGFrZW92ZXIgYXR0YWNrcy4AAAAACmluaXRpYWxpemUAAAAAAAQAAAAAAAAABWFkbWluAAAAAAAAEwAAAAAAAAANZmxvd19jb250cmFjdAAAAAAAABMAAAAAAAAAD2xvY2t1cF9jb250cmFjdAAAAAATAAAAAAAAAAxuZnRfY29udHJhY3QAAAATAAAAAA==", "AAAAAAAAAClBZG1pbiBjYW4gdXBncmFkZSB0aGUgTkZUIGNvbnRyYWN0IGxvZ2ljLgAAAAAAAAt1cGdyYWRlX25mdAAAAAABAAAAAAAAAA1uZXdfd2FzbV9oYXNoAAAAAAAD7gAAACAAAAAA", + "AAAAAAAAABlBY2NlcHQgYW4gYWRtaW4gdHJhbnNmZXIuAAAAAAAADGFjY2VwdF9hZG1pbgAAAAAAAAAA", "AAAAAAAAADBXaXRoZHJhdyBtYXggdG9rZW5zIGZyb20gYSBzdHJlYW0gdXNpbmcgdGhlIE5GVC4AAAAMd2l0aGRyYXdfbWF4AAAAAwAAAAAAAAAIdG9rZW5faWQAAAALAAAAAAAAAAZjYWxsZXIAAAAAABMAAAAAAAAAAnRvAAAAAAATAAAAAQAAAAs=", - "AAAAAAAAADlBdG9taWNhbGx5IGluaXRpYWxpemUgdGhlIFJvdXRlciBhZG1pbiBkdXJpbmcgZGVwbG95bWVudC4AAAAAAAANX19jb25zdHJ1Y3RvcgAAAAAAAAEAAAAAAAAABWFkbWluAAAAAAAAEwAAAAA=", - "AAAAAAAAADpSZXR1cm4gdGhlIGludGVybmFsIGNvcmUgZW5naW5lIElEIGZvciBhIHB1YmxpYyBzdHJlYW0gSUQuAAAAAAAOY29yZV9zdHJlYW1faWQAAAAAAAEAAAAAAAAACHRva2VuX2lkAAAACwAAAAEAAAAG", - "AAAAAAAAACVDcmVhdGUgYSBGbG93IHN0cmVhbSBhbmQgbWludCBhbiBORlQuAAAAAAAAEmNyZWF0ZV9mbG93X3N0cmVhbQAAAAAACAAAAAAAAAAGc2VuZGVyAAAAAAATAAAAAAAAAAlyZWNpcGllbnQAAAAAAAATAAAAAAAAAAV0b2tlbgAAAAAAABMAAAAAAAAAD3JhdGVfcGVyX3NlY29uZAAAAAALAAAAAAAAAA50b2tlbl9kZWNpbWFscwAAAAAABAAAAAAAAAAKc3RhcnRfdGltZQAAAAAABgAAAAAAAAAOaW5pdGlhbF9hbW91bnQAAAAAAAsAAAAAAAAADHRyYW5zZmVyYWJsZQAAAAEAAAABAAAACw==", - "AAAAAAAAACdDcmVhdGUgYSBMb2NrdXAgc3RyZWFtIGFuZCBtaW50IGFuIE5GVC4AAAAAFGNyZWF0ZV9sb2NrdXBfc3RyZWFtAAAAAgAAAAAAAAAGcGFyYW1zAAAAAAfQAAAAEkNyZWF0ZUxvY2t1cFBhcmFtcwAAAAAAAAAAAAx0cmFuc2ZlcmFibGUAAAABAAAAAQAAAAs=", + "AAAAAAAAAChQcm9wb3NlIGEgbmV3IGFkbWluICh0d28tc3RlcCB0cmFuc2ZlcikuAAAADXByb3Bvc2VfYWRtaW4AAAAAAAABAAAAAAAAAAluZXdfYWRtaW4AAAAAAAATAAAAAA==", + "AAAAAAAAADlFeGVjdXRlIGEgcHJldmlvdXNseSBwcm9wb3NlZCB1cGdyYWRlIGFmdGVyIHRoZSB0aW1lbG9jay4AAAAAAAAPZXhlY3V0ZV91cGdyYWRlAAAAAAEAAAAAAAAADW5ld193YXNtX2hhc2gAAAAAAAPuAAAAIAAAAAA=", + "AAAAAAAAAB1Qcm9wb3NlIGEgdGltZWxvY2tlZCB1cGdyYWRlLgAAAAAAAA9wcm9wb3NlX3VwZ3JhZGUAAAAAAQAAAAAAAAANbmV3X3dhc21faGFzaAAAAAAAA+4AAAAgAAAAAA==", + "AAAAAAAAACVDcmVhdGUgYSBGbG93IHN0cmVhbSBhbmQgbWludCBhbiBORlQuAAAAAAAAEmNyZWF0ZV9mbG93X3N0cmVhbQAAAAAABgAAAAAAAAAGc2VuZGVyAAAAAAATAAAAAAAAAAlyZWNpcGllbnQAAAAAAAATAAAAAAAAAAV0b2tlbgAAAAAAABMAAAAAAAAAD3JhdGVfcGVyX3NlY29uZAAAAAALAAAAAAAAAA50b2tlbl9kZWNpbWFscwAAAAAABAAAAAAAAAAKc3RhcnRfdGltZQAAAAAABgAAAAEAAAAL", + "AAAAAAAAACdDcmVhdGUgYSBMb2NrdXAgc3RyZWFtIGFuZCBtaW50IGFuIE5GVC4AAAAAFGNyZWF0ZV9sb2NrdXBfc3RyZWFtAAAAAQAAAAAAAAAGcGFyYW1zAAAAAAfQAAAAEkNyZWF0ZUxvY2t1cFBhcmFtcwAAAAAAAQAAAAs=", "AAAAAQAAA3pDb3JlIGRhdGEgc3RydWN0dXJlIGZvciBhIEZsb3cgKG9wZW4tZW5kZWQsIHJhdGUtcGVyLXNlY29uZCkgc3RyZWFtLgoKUG9ydGVkIGZyb20gU2FibGllcidzIGBGbG93LlN0cmVhbWAgc3RydWN0IHdpdGggdGhlc2UgYWRhcHRhdGlvbnM6CgotIGBVRDIxeDE4IHJhdGVQZXJTZWNvbmRgIOKGkiBgaTEyOCByYXRlX3Blcl9zZWNvbmRgIHNjYWxlZCB0byAxOCBkZWNpbWFscy4KU29yb2JhbiBoYXMgbm8gbmF0aXZlIGZpeGVkLXBvaW50IHR5cGUsIHNvIHdlIHVzZSBpMTI4IHdpdGggbWFudWFsIHNjYWxpbmcuCkEgcmF0ZSBvZiAxIHRva2VuL3NlYyBmb3IgYSA3LWRlY2ltYWwgdG9rZW4gPSAxZTE4IGludGVybmFsbHkuCgotIGBJRVJDMjAgdG9rZW5gIOKGkiBgQWRkcmVzc2AgKFNvcm9iYW4gdG9rZW4gY29udHJhY3QgYWRkcmVzcykuCgotIGB1aW50MTI4IGJhbGFuY2VgIOKGkiBgaTEyOGAgKFNvcm9iYW4gU0RLIGNvbnZlbnRpb24gZm9yIHRva2VuIGFtb3VudHMpLgoKLSBgdWludDQwIHNuYXBzaG90VGltZWAg4oaSIGB1NjRgIChTb3JvYmFuIGxlZGdlciB0aW1lc3RhbXAgaXMgdTY0KS4KCi0gYGJvb2wgaXNTdHJlYW1gIHNlbnRpbmVsIHJlbW92ZWQg4oCUIHdlIHVzZSBzdG9yYWdlIGtleSBleGlzdGVuY2UgaW5zdGVhZC4KCi0gYGJvb2wgaXNUcmFuc2ZlcmFibGVgIHJlbW92ZWQg4oCUIGRlbGVnYXRlZCB0byB0aGUgTkZUIGNvbnRyYWN0IGxheWVyLgoKIyBTdG9yYWdlIExheW91dAoKRWFjaCBzdHJlYW0gaXMgc3RvcmVkIHVuZGVyIGBEYXRhS2V5OjpTdHJlYW0oc3RyZWFtX2lkKWAgaW4gcGVyc2lzdGVudCBzdG9yYWdlLgpUaGUgc3RydWN0IGlzIGtlcHQgYXMgZmxhdCBhcyBwb3NzaWJsZSB0byBtaW5pbWl6ZSBzZXJpYWxpemF0aW9uIG92ZXJoZWFkLgAAAAAAAAAAAApGbG93U3RyZWFtAAAAAAAJAAAATkN1cnJlbnQgYmFsYW5jZSBoZWxkIGluIHRoZSBzdHJlYW0gKGRlcG9zaXRlZCAtIHdpdGhkcmF3biksIGluIHRva2VuIGRlY2ltYWxzLgAAAAAAB2JhbGFuY2UAAAAACwAAAC9XaGV0aGVyIHRoZSBzdHJlYW0gaGFzIGJlZW4gcGVybWFuZW50bHkgdm9pZGVkLgAAAAAJaXNfdm9pZGVkAAAAAAAAAQAAAKVSYXRlIGF0IHdoaWNoIGRlYnQgYWNjcnVlcywgaW4gMTgtZGVjaW1hbCBmaXhlZC1wb2ludC4KMCBtZWFucyB0aGUgc3RyZWFtIGlzIHBhdXNlZC4KRXhhbXBsZTogMSB0b2tlbi9zZWMgZm9yIGEgNy1kZWNpbWFsIHRva2VuIOKGkiAxXzAwMF8wMDBfMDAwXzAwMF8wMDBfMDAwICgxZTE4KS4AAAAAAAAPcmF0ZV9wZXJfc2Vjb25kAAAAAAsAAAAwVGhlIGFkZHJlc3MgcmVjZWl2aW5nIHRoZSB0b2tlbnMgKGNhbiB3aXRoZHJhdykuAAAACXJlY2lwaWVudAAAAAAAABMAAAA9VGhlIGFkZHJlc3Mgc3RyZWFtaW5nIHRoZSB0b2tlbnMgKGNhbiBwYXVzZSwgYWRqdXN0LCByZWZ1bmQpLgAAAAAAAAZzZW5kZXIAAAAAABMAAAB6QWNjdW11bGF0ZWQgZGVidCBhdCB0aGUgbGFzdCBzbmFwc2hvdCwgaW4gMTgtZGVjaW1hbCBmaXhlZC1wb2ludC4KVG90YWwgZGVidCA9IHNuYXBzaG90X2RlYnRfc2NhbGVkICsgb25nb2luZ19kZWJ0X3NjYWxlZC4AAAAAABRzbmFwc2hvdF9kZWJ0X3NjYWxlZAAAAAsAAABkVW5peCB0aW1lc3RhbXAgb2YgdGhlIGxhc3QgZGVidCBzbmFwc2hvdC4KRGVidCBhY2NydWVzIGZyb20gdGhpcyBwb2ludCBmb3J3YXJkIGF0IGByYXRlX3Blcl9zZWNvbmRgLgAAAA1zbmFwc2hvdF90aW1lAAAAAAAABgAAADpUaGUgU29yb2JhbiB0b2tlbiBjb250cmFjdCBhZGRyZXNzIChTQUMgb3IgY3VzdG9tIFNFUC00MSkuAAAAAAAFdG9rZW4AAAAAAAATAAAAbU51bWJlciBvZiBkZWNpbWFscyBmb3IgdGhlIHRva2VuIChlLmcuIDcgZm9yIG1vc3QgU3RlbGxhciBhc3NldHMpLgpVc2VkIGZvciBzY2FsZS9kZXNjYWxlIG9wZXJhdGlvbnMuIE1heCAxOC4AAAAAAAAOdG9rZW5fZGVjaW1hbHMAAAAAAAQ=", "AAAAAwAAAGhUeXBlIG9mIHN0cmVhbSAoRmxvdyBvciBMb2NrdXApLgpVc2VkIGJ5IHRoZSBTdHJlYW0gTkZUIGFuZCBSb3V0ZXIgdG8gcm91dGUgbG9naWMgdG8gdGhlIGNvcnJlY3QgZW5naW5lLgAAAAAAAAAKU3RyZWFtVHlwZQAAAAAAAgAAAAAAAAAERmxvdwAAAAAAAAAAAAAABkxvY2t1cAAAAAAAAQ==", "AAAAAwAAAMtTdGF0dXMgb2YgYSBMb2NrdXAgc3RyZWFtLgoKVXNlcyAidGVtcGVyYXR1cmUiIHNlbWFudGljczoKLSAqKldhcm0qKiAoUGVuZGluZywgU3RyZWFtaW5nKTogdGltZSBhbG9uZSBjYW4gY2hhbmdlIHRoZSBzdGF0dXMuCi0gKipDb2xkKiogKFNldHRsZWQsIENhbmNlbGVkLCBEZXBsZXRlZCk6IHRpbWUgYWxvbmUgY2Fubm90IGNoYW5nZSB0aGUgc3RhdHVzLgAAAAAAAAAADExvY2t1cFN0YXR1cwAAAAUAAAA/Q3JlYXRlZCBidXQgc3RhcnRfdGltZSBpcyBpbiB0aGUgZnV0dXJlLiBObyB0b2tlbnMgaGF2ZSB2ZXN0ZWQuAAAAAAdQZW5kaW5nAAAAAAAAAAAyQWN0aXZlIOKAlCB0b2tlbnMgYXJlIGN1cnJlbnRseSB2ZXN0aW5nIG92ZXIgdGltZS4AAAAAAAlTdHJlYW1pbmcAAAAAAAABAAAAS0FsbCB0b2tlbnMgaGF2ZSBmdWxseSB2ZXN0ZWQuIFJlY2lwaWVudCBjYW4gd2l0aGRyYXcgdGhlIHJlbWFpbmluZyBiYWxhbmNlLgAAAAAHU2V0dGxlZAAAAAACAAAAP1NlbmRlciBjYW5jZWxlZCB0aGUgc3RyZWFtLiBVbnZlc3RlZCB0b2tlbnMgcmV0dXJuZWQgdG8gc2VuZGVyLgAAAAAIQ2FuY2VsZWQAAAADAAAAQkZ1bGx5IHdpdGhkcmF3biAoYW5kL29yIHJlZnVuZGVkKS4gTm8gdG9rZW5zIHJlbWFpbiBpbiB0aGUgc3RyZWFtLgAAAAAACERlcGxldGVkAAAABA==", "AAAAAQAAAtxDb3JlIGRhdGEgc3RydWN0dXJlIGZvciBhIExvY2t1cCAoZml4ZWQtdGVybSB2ZXN0aW5nKSBzdHJlYW0uCgpTdXBwb3J0cyBsaW5lYXIgdW5sb2NrIHdpdGggb3B0aW9uYWwgY2xpZmYuIFRoZSB2ZXN0ZWQgKCJzdHJlYW1lZCIpIGFtb3VudAphdCBhbnkgdGltZSBgdGAgaXMgY2FsY3VsYXRlZCBhczoKCmBgYHRleHQKaWYgdCA8IHN0YXJ0X3RpbWU6ICAgICAgIHZlc3RlZCA9IDAKaWYgdCA8IGNsaWZmX3RpbWU6ICAgICAgIHZlc3RlZCA9IHN0YXJ0X3VubG9ja19hbW91bnQKaWYgdCA+PSBlbmRfdGltZTogICAgICAgICB2ZXN0ZWQgPSB0b3RhbF9hbW91bnQKZWxzZToKZWxhcHNlZCA9IGZsb29yKCh0IC0gY2xpZmZfdGltZSkgLyBncmFudWxhcml0eSkgKiBncmFudWxhcml0eQpzdHJlYW1hYmxlX2R1cmF0aW9uID0gZW5kX3RpbWUgLSBjbGlmZl90aW1lCnN0cmVhbWFibGVfYW1vdW50ID0gdG90YWxfYW1vdW50IC0gc3RhcnRfdW5sb2NrX2Ftb3VudCAtIGNsaWZmX3VubG9ja19hbW91bnQKdmVzdGVkID0gc3RhcnRfdW5sb2NrX2Ftb3VudCArIGNsaWZmX3VubG9ja19hbW91bnQgKyAoZWxhcHNlZCAqIHN0cmVhbWFibGVfYW1vdW50IC8gc3RyZWFtYWJsZV9kdXJhdGlvbikKYGBgCgpUaGlzIG1pcnJvcnMgdGhlIHJlZmVyZW5jZSBsaW5lYXIgbG9ja3VwIGNhbGN1bGF0aW9uIHdpdGggZGlzY3JldGUgdW5sb2NrCnN0ZXBzIGF0IGBncmFudWxhcml0eWAtc2Vjb25kIGludGVydmFscy4AAAAAAAAADExvY2t1cFN0cmVhbQAAAA8AAABGV2hldGhlciB0aGUgc2VuZGVyIGNhbiBjYW5jZWwgdGhpcyBzdHJlYW0gYW5kIHJlY2xhaW0gdW52ZXN0ZWQgdG9rZW5zLgAAAAAACmNhbmNlbGFibGUAAAAAAAEAAABuT3B0aW9uYWwgY2xpZmYgdGltZXN0YW1wLiBObyB0b2tlbnMgYmV5b25kIGBzdGFydF91bmxvY2tfYW1vdW50YAp2ZXN0IGJlZm9yZSB0aGlzIHRpbWUuIFNldCB0byAwIGZvciBubyBjbGlmZi4AAAAAAApjbGlmZl90aW1lAAAAAAAGAAAAR0Ftb3VudCB1bmxvY2tlZCBhdCBgY2xpZmZfdGltZWAgKGluIGFkZGl0aW9uIHRvIGBzdGFydF91bmxvY2tfYW1vdW50YCkuAAAAABNjbGlmZl91bmxvY2tfYW1vdW50AAAAAAsAAAAxVW5peCB0aW1lc3RhbXAgd2hlbiB0aGUgbG9ja3VwIGlzIGZ1bGx5IHVubG9ja2VkLgAAAAAAAAhlbmRfdGltZQAAAAYAAAB9VW5sb2NrIGdyYW51bGFyaXR5IGluIHNlY29uZHMuIFRva2VucyB2ZXN0IGluIGRpc2NyZXRlIHN0ZXBzIG9mIHRoaXMKaW50ZXJ2YWwuIERlZmF1bHQgPSAxIChwZXItc2Vjb25kIHZlc3RpbmcpLiBNdXN0IGJlID4gMC4AAAAAAAALZ3JhbnVsYXJpdHkAAAAABgAAADdXaGV0aGVyIGFsbCB0b2tlbnMgaGF2ZSBiZWVuIHdpdGhkcmF3biBhbmQvb3IgcmVmdW5kZWQuAAAAAAtpc19kZXBsZXRlZAAAAAABAAAAP1RoZSBhZGRyZXNzIHRoYXQgcmVjZWl2ZXMgdG9rZW5zIGFzIHRoZXkgdW5sb2NrIChjYW4gd2l0aGRyYXcpLgAAAAAJcmVjaXBpZW50AAAAAAAAEwAAAEFBbW91bnQgcmVmdW5kZWQgdG8gc2VuZGVyIG9uIGNhbmNlbGxhdGlvbi4gWmVybyB1bmxlc3MgY2FuY2VsbGVkLgAAAAAAAA9yZWZ1bmRlZF9hbW91bnQAAAAACwAAAExUaGUgYWRkcmVzcyB0aGF0IGNyZWF0ZWQgYW5kIGZ1bmRlZCB0aGUgbG9ja3VwIChjYW4gY2FuY2VsIGlmIGBjYW5jZWxhYmxlYCkuAAAABnNlbmRlcgAAAAAAEwAAACZVbml4IHRpbWVzdGFtcCB3aGVuIHRoZSBsb2NrdXAgYmVnaW5zLgAAAAAACnN0YXJ0X3RpbWUAAAAAAAYAAAAsQW1vdW50IHVubG9ja2VkIGltbWVkaWF0ZWx5IGF0IGBzdGFydF90aW1lYC4AAAATc3RhcnRfdW5sb2NrX2Ftb3VudAAAAAALAAAAI1RoZSBTb3JvYmFuIHRva2VuIGNvbnRyYWN0IGFkZHJlc3MuAAAAAAV0b2tlbgAAAAAAABMAAAA7VG90YWwgYW1vdW50IGRlcG9zaXRlZCBpbnRvIHRoZSBzdHJlYW0gKGluIHRva2VuIGRlY2ltYWxzKS4AAAAADHRvdGFsX2Ftb3VudAAAAAsAAAAmV2hldGhlciB0aGUgc3RyZWFtIGhhcyBiZWVuIGNhbmNlbGxlZC4AAAAAAAx3YXNfY2FuY2VsZWQAAAABAAAALUN1bXVsYXRpdmUgYW1vdW50IHdpdGhkcmF3biBieSB0aGUgcmVjaXBpZW50LgAAAAAAABB3aXRoZHJhd25fYW1vdW50AAAACw==", "AAAAAwAAALFTdGF0dXMgb2YgYSBGbG93IHN0cmVhbS4KClRoZSBzdGF0dXMgaXMgZGVyaXZlZCBmcm9tIHRoZSBzdHJlYW0ncyBjdXJyZW50IHN0YXRlIChyYXRlLCBiYWxhbmNlLCBkZWJ0LCB2b2lkZWQgZmxhZykgcmF0aGVyIHRoYW4KYmVpbmcgc3RvcmVkIGRpcmVjdGx5IOKAlCBrZWVwaW5nIHN0b3JhZ2UgbWluaW1hbC4AAAAAAAAAAAAADFN0cmVhbVN0YXR1cwAAAAYAAAA+U3RyZWFtIHNjaGVkdWxlZCB0byBzdGFydCBpbiB0aGUgZnV0dXJlIChzbmFwc2hvdF90aW1lID4gbm93KS4AAAAAAAdQZW5kaW5nAAAAAAAAAAAyQWN0aXZlbHkgc3RyZWFtaW5nIHdpdGggYmFsYW5jZSBjb3ZlcmluZyBhbGwgZGVidC4AAAAAABBTdHJlYW1pbmdTb2x2ZW50AAAAAQAAADZBY3RpdmVseSBzdHJlYW1pbmcgYnV0IGRlYnQgZXhjZWVkcyBhdmFpbGFibGUgYmFsYW5jZS4AAAAAABJTdHJlYW1pbmdJbnNvbHZlbnQAAAAAAAIAAAAoUGF1c2VkIGJ5IHNlbmRlciB3aXRoIG5vIHVuY292ZXJlZCBkZWJ0LgAAAA1QYXVzZWRTb2x2ZW50AAAAAAAAAwAAACVQYXVzZWQgYnkgc2VuZGVyIHdpdGggdW5jb3ZlcmVkIGRlYnQuAAAAAAAAD1BhdXNlZEluc29sdmVudAAAAAAEAAAARVBlcm1hbmVudGx5IHN0b3BwZWQuIENhbm5vdCBiZSByZXN0YXJ0ZWQuIFVuY292ZXJlZCBkZWJ0IHdyaXR0ZW4gb2ZmLgAAAAAAAAZWb2lkZWQAAAAAAAU=", - "AAAAAQAAAEdTdGFibGUgcHVibGljIG1ldGFkYXRhIGZvciBhIFN0cmVhbSBORlQgYW5kIGl0cyB1bmRlcmx5aW5nIGNvcmUgc3RyZWFtLgAAAAAAAAAADlN0cmVhbU1ldGFkYXRhAAAAAAAGAAAAOUludGVybmFsIGlkZW50aWZpZXIgYXNzaWduZWQgYnkgdGhlIHNlbGVjdGVkIGNvcmUgZW5naW5lLgAAAAAAAA5jb3JlX3N0cmVhbV9pZAAAAAAABgAAADxDdXJyZW50IGhvbGRlciBvZiB3aXRoZHJhd2FsIGFuZCBORlQtb3duZXIgRmxvdyB2b2lkIHJpZ2h0cy4AAAAFb3duZXIAAAAAAAATAAAAJ0Nhbm9uaWNhbCBjcm9zcy1lbmdpbmUgbGlmZWN5Y2xlIHN0YXRlLgAAAAAGc3RhdHVzAAAAAAfQAAAAFUNhbm9uaWNhbFN0cmVhbVN0YXR1cwAAAAAAABFDb3JlIGVuZ2luZSBraW5kLgAAAAAAAAtzdHJlYW1fdHlwZQAAAAfQAAAAClN0cmVhbVR5cGUAAAAAACxQdWJsaWMsIGRlcGxveW1lbnQtc2NvcGVkIHN0cmVhbSBpZGVudGlmaWVyLgAAAAh0b2tlbl9pZAAAAAsAAABFSW1tdXRhYmxlIHRyYW5zZmVyYWJpbGl0eSBwb2xpY3kgc2VsZWN0ZWQgd2hlbiB0aGUgc3RyZWFtIGlzIGNyZWF0ZWQuAAAAAAAADHRyYW5zZmVyYWJsZQAAAAE=", "AAAAAQAAAINQYXJhbWV0ZXJzIGZvciBjcmVhdGluZyBhIG5ldyBMb2NrdXAgc3RyZWFtLgoKQnVuZGxlZCBpbnRvIGEgc3RydWN0IGJlY2F1c2UgU29yb2JhbiBjb250cmFjdCBmdW5jdGlvbnMKaGF2ZSBhIG1heCBvZiAxMCBwYXJhbWV0ZXJzLgAAAAAAAAAAEkNyZWF0ZUxvY2t1cFBhcmFtcwAAAAAACwAAAClXaGV0aGVyIHRoZSBzZW5kZXIgY2FuIGNhbmNlbCB0aGUgc3RyZWFtLgAAAAAAAApjYW5jZWxhYmxlAAAAAAABAAAAME9wdGlvbmFsIGNsaWZmIHRpbWVzdGFtcC4gU2V0IHRvIDAgZm9yIG5vIGNsaWZmLgAAAApjbGlmZl90aW1lAAAAAAAGAAAAKlRva2VucyB1bmxvY2tlZCBhdCBjbGlmZiAoYWRkZWQgdG8gc3RhcnQpLgAAAAAAE2NsaWZmX3VubG9ja19hbW91bnQAAAAACwAAACZVbml4IHRpbWVzdGFtcCB3aGVuIHZlc3RpbmcgY29tcGxldGVzLgAAAAAACGVuZF90aW1lAAAABgAAADFVbmxvY2sgc3RlcCBpbnRlcnZhbCBpbiBzZWNvbmRzLiAwIGRlZmF1bHRzIHRvIDEuAAAAAAAAC2dyYW51bGFyaXR5AAAAAAYAAAAgQWRkcmVzcyByZWNlaXZpbmcgdmVzdGVkIHRva2Vucy4AAAAJcmVjaXBpZW50AAAAAAAAEwAAADhBZGRyZXNzIGZ1bmRpbmcgdGhlIHN0cmVhbSAoY2FuIGNhbmNlbCBpZiBgY2FuY2VsYWJsZWApLgAAAAZzZW5kZXIAAAAAABMAAAAjVW5peCB0aW1lc3RhbXAgd2hlbiB2ZXN0aW5nIGJlZ2lucy4AAAAACnN0YXJ0X3RpbWUAAAAAAAYAAAAlVG9rZW5zIHVubG9ja2VkIGltbWVkaWF0ZWx5IGF0IHN0YXJ0LgAAAAAAABNzdGFydF91bmxvY2tfYW1vdW50AAAAAAsAAAAfU29yb2JhbiB0b2tlbiBjb250cmFjdCBhZGRyZXNzLgAAAAAFdG9rZW4AAAAAAAATAAAAKVRvdGFsIHRva2VucyB0byB2ZXN0IChpbiB0b2tlbiBkZWNpbWFscykuAAAAAAAADHRvdGFsX2Ftb3VudAAAAAs=", - "AAAAAwAAAMZDYW5vbmljYWwgbGlmZWN5Y2xlIGV4cG9zZWQgYnkgdGhlIFJvdXRlciB0byBldmVyeSBwcm9kdWN0IHN1cmZhY2UuCgpDb3JlIGVuZ2luZXMgcmV0YWluIHRoZWlyIG1vcmUgZGV0YWlsZWQgYWNjb3VudGluZyBzdGF0ZXM7IHRoZSBSb3V0ZXIgbWFwcwp0aG9zZSBzdGF0ZXMgaW50byB0aGlzIHN0YWJsZSBjcm9zcy1lbmdpbmUgdm9jYWJ1bGFyeS4AAAAAAAAAAAAVQ2Fub25pY2FsU3RyZWFtU3RhdHVzAAAAAAAABgAAAAAAAAAHUGVuZGluZwAAAAAAAAAAAAAAAAZBY3RpdmUAAAAAAAEAAAAAAAAABlBhdXNlZAAAAAAAAgAAAAAAAAAIQ2FuY2VsZWQAAAADAAAAAAAAAAlDb21wbGV0ZWQAAAAAAAAEAAAAAAAAAAZGYWlsZWQAAAAAAAU=", "AAAABAAAACtFcnJvcnMgc3BlY2lmaWMgdG8gdGhlIFN0cmVhbSBORlQgY29udHJhY3QuAAAAAAAAAAAITmZ0RXJyb3IAAAAFAAAAAAAAABJBbHJlYWR5SW5pdGlhbGl6ZWQAAAAAAMkAAAAAAAAADU5vdEF1dGhvcml6ZWQAAAAAAADKAAAAAAAAAA1Ub2tlbk5vdEZvdW5kAAAAAAAAywAAAAAAAAAPTm90VHJhbnNmZXJhYmxlAAAAAMwAAAAhVG9rZW4gSUQgaGFzIGFscmVhZHkgYmVlbiBtaW50ZWQuAAAAAAAADUFscmVhZHlNaW50ZWQAAAAAAADN", - "AAAABAAAAC5FcnJvcnMgZW1pdHRlZCBieSB0aGUgRmxvdyBzdHJlYW1pbmcgY29udHJhY3QuAAAAAAAAAAAACUZsb3dFcnJvcgAAAAAAABYAAAAZU3RyZWFtIElEIGRvZXMgbm90IGV4aXN0LgAAAAAAAA5TdHJlYW1Ob3RGb3VuZAAAAAAAAQAAACxDYWxsZXIgaXMgbm90IGF1dGhvcml6ZWQgZm9yIHRoaXMgb3BlcmF0aW9uLgAAAAxVbmF1dGhvcml6ZWQAAAACAAAANlN0cmVhbSBpcyBwYXVzZWQ7IG9wZXJhdGlvbiByZXF1aXJlcyBhY3RpdmUgc3RyZWFtaW5nLgAAAAAADFN0cmVhbVBhdXNlZAAAAAMAAAAvU3RyZWFtIGlzIHZvaWRlZDsgbm8gZnVydGhlciBtdXRhdGlvbnMgYWxsb3dlZC4AAAAADFN0cmVhbVZvaWRlZAAAAAQAAAA3U3RyZWFtIGlzIG5vdCBwYXVzZWQ7IHJlc3RhcnQgcmVxdWlyZXMgYSBwYXVzZWQgc3RyZWFtLgAAAAAPU3RyZWFtTm90UGF1c2VkAAAAAAUAAAA5U3RyZWFtIGhhcyBub3Qgc3RhcnRlZCB5ZXQgKHNuYXBzaG90X3RpbWUgaW4gdGhlIGZ1dHVyZSkuAAAAAAAADVN0cmVhbVBlbmRpbmcAAAAAAAAGAAAAIE5ldyByYXRlIHBlciBzZWNvbmQgbXVzdCBiZSA+IDAuAAAAEVJhdGVQZXJTZWNvbmRaZXJvAAAAAAAABwAAACdOZXcgcmF0ZSBtdXN0IGRpZmZlciBmcm9tIGN1cnJlbnQgcmF0ZS4AAAAAEFJhdGVOb3REaWZmZXJlbnQAAAAIAAAAG0RlcG9zaXQgYW1vdW50IG11c3QgYmUgPiAwLgAAAAARRGVwb3NpdEFtb3VudFplcm8AAAAAAAAJAAAAHFdpdGhkcmF3IGFtb3VudCBtdXN0IGJlID4gMC4AAAASV2l0aGRyYXdBbW91bnRaZXJvAAAAAAAKAAAALVdpdGhkcmF3IGFtb3VudCBleGNlZWRzIHdpdGhkcmF3YWJsZSBiYWxhbmNlLgAAAAAAAAhPdmVyZHJhdwAAAAsAAAAaUmVmdW5kIGFtb3VudCBtdXN0IGJlID4gMC4AAAAAABBSZWZ1bmRBbW91bnRaZXJvAAAADAAAAClSZWZ1bmQgYW1vdW50IGV4Y2VlZHMgcmVmdW5kYWJsZSBiYWxhbmNlLgAAAAAAAA5SZWZ1bmRPdmVyZmxvdwAAAAAADQAAACVUb2tlbiBoYXMgPiAxOCBkZWNpbWFscywgdW5zdXBwb3J0ZWQuAAAAAAAAFEludmFsaWRUb2tlbkRlY2ltYWxzAAAADgAAADZTdHJlYW0gYmFsYW5jZSBpcyB6ZXJvIChlLmcuIHF1ZXJ5aW5nIGRlcGxldGlvbiB0aW1lKS4AAAAAAAtCYWxhbmNlWmVybwAAAAAPAAAAHUNvbnRyYWN0IGFscmVhZHkgaW5pdGlhbGl6ZWQuAAAAAAAAEkFscmVhZHlJbml0aWFsaXplZAAAAAAAEAAAAB1Db250cmFjdCBub3QgeWV0IGluaXRpYWxpemVkLgAAAAAAAA5Ob3RJbml0aWFsaXplZAAAAAAAEQAAADhDYW5ub3QgY3JlYXRlIGEgcGVuZGluZyBzdHJlYW0gd2l0aCByYXRlX3Blcl9zZWNvbmQgPSAwLgAAABdDcmVhdGVSYXRlUGVyU2Vjb25kWmVybwAAAAASAAAAOUludGVybmFsIG1hdGggZXJyb3Ig4oCUIHNob3VsZCBuZXZlciBvY2N1ciBpbiBwcm9kdWN0aW9uLgAAAAAAABJJbnZhbGlkQ2FsY3VsYXRpb24AAAAAABMAAAAxU2VuZGVyIGFuZCByZWNpcGllbnQgbXVzdCBiZSBkaWZmZXJlbnQgYWRkcmVzc2VzLgAAAAAAABVTZW5kZXJFcXVhbHNSZWNpcGllbnQAAAAAAAAUAAAAJVJhdGUgcGVyIHNlY29uZCBtdXN0IG5vdCBiZSBuZWdhdGl2ZS4AAAAAAAAMTmVnYXRpdmVSYXRlAAAAFQAAAD5UaGUgdG9rZW4gZGlkIG5vdCBkZWJpdCBhbmQgY3JlZGl0IHRoZSBleGFjdCByZXF1ZXN0ZWQgYW1vdW50LgAAAAAAFVRva2VuVHJhbnNmZXJNaXNtYXRjaAAAAAAAABY=", - "AAAABAAAAC5FcnJvcnMgZW1pdHRlZCBieSB0aGUgTG9ja3VwIHZlc3RpbmcgY29udHJhY3QuAAAAAAAAAAAAC0xvY2t1cEVycm9yAAAAAAwAAAAZU3RyZWFtIElEIGRvZXMgbm90IGV4aXN0LgAAAAAAAA5TdHJlYW1Ob3RGb3VuZAAAAAAAZQAAABlDYWxsZXIgaXMgbm90IGF1dGhvcml6ZWQuAAAAAAAADFVuYXV0aG9yaXplZAAAAGYAAAAcU3RyZWFtIGlzIGFscmVhZHkgY2FuY2VsbGVkLgAAABBBbHJlYWR5Q2FuY2VsbGVkAAAAZwAAABlTdHJlYW0gaXMgbm90IGNhbmNlbGFibGUuAAAAAAAADU5vdENhbmNlbGFibGUAAAAAAABoAAAAKVdpdGhkcmF3IGFtb3VudCBleGNlZWRzIHVubG9ja2VkIGJhbGFuY2UuAAAAAAAACE92ZXJkcmF3AAAAaQAAADxJbnZhbGlkIHRpbWUgcGFyYW1ldGVycyAoc3RhcnQgPj0gZW5kLCBjbGlmZiBvdXRzaWRlIHJhbmdlKS4AAAAQSW52YWxpZFRpbWVSYW5nZQAAAGoAAAAZVG90YWwgYW1vdW50IG11c3QgYmUgPiAwLgAAAAAAAApBbW91bnRaZXJvAAAAAABrAAAAHUNvbnRyYWN0IGFscmVhZHkgaW5pdGlhbGl6ZWQuAAAAAAAAEkFscmVhZHlJbml0aWFsaXplZAAAAAAAbAAAAB1Db250cmFjdCBub3QgeWV0IGluaXRpYWxpemVkLgAAAAAAAA5Ob3RJbml0aWFsaXplZAAAAAAAbQAAADFTZW5kZXIgYW5kIHJlY2lwaWVudCBtdXN0IGJlIGRpZmZlcmVudCBhZGRyZXNzZXMuAAAAAAAAFVNlbmRlckVxdWFsc1JlY2lwaWVudAAAAAAAAG4AAABLU3RhcnQgYW5kIGNsaWZmIHVubG9jayBhbW91bnRzIG11c3QgYmUgbm9ubmVnYXRpdmUgYW5kIGZpdCBpbiB0b3RhbCBhbW91bnQuAAAAABNJbnZhbGlkVW5sb2NrQW1vdW50AAAAAG8AAAA+VGhlIHRva2VuIGRpZCBub3QgZGViaXQgYW5kIGNyZWRpdCB0aGUgZXhhY3QgcmVxdWVzdGVkIGFtb3VudC4AAAAAABVUb2tlblRyYW5zZmVyTWlzbWF0Y2gAAAAAAABw", - "AAAABAAAACdFcnJvcnMgc3BlY2lmaWMgdG8gdGhlIFJvdXRlciBjb250cmFjdC4AAAAAAAAAAAtSb3V0ZXJFcnJvcgAAAAAFAAAAAAAAABJBbHJlYWR5SW5pdGlhbGl6ZWQAAAAAAS0AAAAAAAAADk5vdEluaXRpYWxpemVkAAAAAAEuAAAAAAAAAA1Ob3RBdXRob3JpemVkAAAAAAABLwAAAAAAAAARSW52YWxpZFN0cmVhbVR5cGUAAAAAAAEwAAAAMENvcmUgY29udHJhY3QgYWRkcmVzc2VzIHdlcmUgYWxyZWFkeSBjb25maWd1cmVkLgAAABFBbHJlYWR5Q29uZmlndXJlZAAAAAAAATE=", - "AAAAAgAAAKFTdG9yYWdlIGtleXMgZm9yIGNvbnRyYWN0IGRhdGEuCgpVc2luZyBhIHR5cGVkIGVudW0gcHJldmVudHMga2V5IGNvbGxpc2lvbnMgKFNLSUxMLm1kIMKnMykuCktleXMgYXJlIG5hbWVzcGFjZWQgYnkgdmFyaWFudCB0byBrZWVwIGRpZmZlcmVudCBkYXRhIHR5cGVzIHNlcGFyYXRlLgAAAAAAAAAAAAAHRGF0YUtleQAAAAAPAAAAAAAAACFBZG1pbiBhZGRyZXNzIChJbnN0YW5jZSBzdG9yYWdlKS4AAAAAAAAFQWRtaW4AAAAAAAAAAAAAKk5leHQgc3RyZWFtIElEIGNvdW50ZXIgKEluc3RhbmNlIHN0b3JhZ2UpLgAAAAAADE5leHRTdHJlYW1JZAAAAAEAAAA+QSBGbG93IHN0cmVhbSByZWNvcmQsIGtleWVkIGJ5IHN0cmVhbSBJRCAoUGVyc2lzdGVudCBzdG9yYWdlKS4AAAAAAApGbG93U3RyZWFtAAAAAAABAAAABgAAAAEAAABAQSBMb2NrdXAgc3RyZWFtIHJlY29yZCwga2V5ZWQgYnkgc3RyZWFtIElEIChQZXJzaXN0ZW50IHN0b3JhZ2UpLgAAAAxMb2NrdXBTdHJlYW0AAAABAAAABgAAAAEAAACTQWdncmVnYXRlIHRva2VuIGJhbGFuY2UgaGVsZCBieSB0aGUgY29udHJhY3QgZm9yIGEgZ2l2ZW4gdG9rZW4gYWRkcmVzcy4KVXNlZCBmb3Igc3VycGx1cyByZWNvdmVyeSAvIGFjY291bnRpbmcgcmVjb25jaWxpYXRpb24gKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAAABBBZ2dyZWdhdGVCYWxhbmNlAAAAAQAAABMAAAABAAAAOE5GVCB0b2tlbiBvd25lciwga2V5ZWQgYnkgdG9rZW4gSUQgKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAAClRva2VuT3duZXIAAAAAAAEAAAALAAAAAQAAAExORlQgc3RyZWFtIGRhdGEgbWFwcGluZyB0b2tlbiBJRCB0byBzdHJlYW0gdHlwZSBhbmQgSUQgKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAAD1Rva2VuU3RyZWFtRGF0YQAAAAABAAAACwAAAAEAAABFSW1tdXRhYmxlIHBlci1zdHJlYW0gTkZUIHRyYW5zZmVyYWJpbGl0eSBwb2xpY3kgKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAAAAAAEVRva2VuVHJhbnNmZXJhYmxlAAAAAAAAAQAAAAsAAAABAAAAOE51bWJlciBvZiBORlRzIG93bmVkIGJ5IGFuIGFkZHJlc3MgKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAACk5mdEJhbGFuY2UAAAAAAAEAAAATAAAAAQAAADtUb2tlbiBtZXRhZGF0YSwgZS5nLiwgbmFtZSwgc3ltYm9sLCBVUkkgKEluc3RhbmNlIHN0b3JhZ2UpLgAAAAANVG9rZW5NZXRhZGF0YQAAAAAAAAEAAAARAAAAAAAAACxSb3V0ZXIgY29uZmlndXJhdGlvbjogRmxvdyBjb250cmFjdCBhZGRyZXNzLgAAAAxGbG93Q29udHJhY3QAAAAAAAAALlJvdXRlciBjb25maWd1cmF0aW9uOiBMb2NrdXAgY29udHJhY3QgYWRkcmVzcy4AAAAAAA5Mb2NrdXBDb250cmFjdAAAAAAAAAAAACtSb3V0ZXIgY29uZmlndXJhdGlvbjogTkZUIGNvbnRyYWN0IGFkZHJlc3MuAAAAAAtOZnRDb250cmFjdAAAAAAAAAAAO0NvcmUtZW5naW5lIGNvbmZpZ3VyYXRpb246IHRydXN0ZWQgUm91dGVyIGNvbnRyYWN0IGFkZHJlc3MuAAAAAAZSb3V0ZXIAAAAAAAAAAAA9UGF5bWFzdGVyIGNvbmZpZ3VyYXRpb246IGxpc3Qgb2YgYWxsb3dlZCBmZWUgdG9rZW4gYWRkcmVzc2VzLgAAAAAAABBBbGxvd2VkRmVlVG9rZW5z" ]), + "AAAABAAAAC5FcnJvcnMgZW1pdHRlZCBieSB0aGUgRmxvdyBzdHJlYW1pbmcgY29udHJhY3QuAAAAAAAAAAAACUZsb3dFcnJvcgAAAAAAABwAAAAZU3RyZWFtIElEIGRvZXMgbm90IGV4aXN0LgAAAAAAAA5TdHJlYW1Ob3RGb3VuZAAAAAAAAQAAACxDYWxsZXIgaXMgbm90IGF1dGhvcml6ZWQgZm9yIHRoaXMgb3BlcmF0aW9uLgAAAAxVbmF1dGhvcml6ZWQAAAACAAAANlN0cmVhbSBpcyBwYXVzZWQ7IG9wZXJhdGlvbiByZXF1aXJlcyBhY3RpdmUgc3RyZWFtaW5nLgAAAAAADFN0cmVhbVBhdXNlZAAAAAMAAAAvU3RyZWFtIGlzIHZvaWRlZDsgbm8gZnVydGhlciBtdXRhdGlvbnMgYWxsb3dlZC4AAAAADFN0cmVhbVZvaWRlZAAAAAQAAAA3U3RyZWFtIGlzIG5vdCBwYXVzZWQ7IHJlc3RhcnQgcmVxdWlyZXMgYSBwYXVzZWQgc3RyZWFtLgAAAAAPU3RyZWFtTm90UGF1c2VkAAAAAAUAAAA5U3RyZWFtIGhhcyBub3Qgc3RhcnRlZCB5ZXQgKHNuYXBzaG90X3RpbWUgaW4gdGhlIGZ1dHVyZSkuAAAAAAAADVN0cmVhbVBlbmRpbmcAAAAAAAAGAAAAIE5ldyByYXRlIHBlciBzZWNvbmQgbXVzdCBiZSA+IDAuAAAAEVJhdGVQZXJTZWNvbmRaZXJvAAAAAAAABwAAACdOZXcgcmF0ZSBtdXN0IGRpZmZlciBmcm9tIGN1cnJlbnQgcmF0ZS4AAAAAEFJhdGVOb3REaWZmZXJlbnQAAAAIAAAAG0RlcG9zaXQgYW1vdW50IG11c3QgYmUgPiAwLgAAAAARRGVwb3NpdEFtb3VudFplcm8AAAAAAAAJAAAAHFdpdGhkcmF3IGFtb3VudCBtdXN0IGJlID4gMC4AAAASV2l0aGRyYXdBbW91bnRaZXJvAAAAAAAKAAAALVdpdGhkcmF3IGFtb3VudCBleGNlZWRzIHdpdGhkcmF3YWJsZSBiYWxhbmNlLgAAAAAAAAhPdmVyZHJhdwAAAAsAAAAaUmVmdW5kIGFtb3VudCBtdXN0IGJlID4gMC4AAAAAABBSZWZ1bmRBbW91bnRaZXJvAAAADAAAAClSZWZ1bmQgYW1vdW50IGV4Y2VlZHMgcmVmdW5kYWJsZSBiYWxhbmNlLgAAAAAAAA5SZWZ1bmRPdmVyZmxvdwAAAAAADQAAACVUb2tlbiBoYXMgPiAxOCBkZWNpbWFscywgdW5zdXBwb3J0ZWQuAAAAAAAAFEludmFsaWRUb2tlbkRlY2ltYWxzAAAADgAAADZTdHJlYW0gYmFsYW5jZSBpcyB6ZXJvIChlLmcuIHF1ZXJ5aW5nIGRlcGxldGlvbiB0aW1lKS4AAAAAAAtCYWxhbmNlWmVybwAAAAAPAAAAHUNvbnRyYWN0IGFscmVhZHkgaW5pdGlhbGl6ZWQuAAAAAAAAEkFscmVhZHlJbml0aWFsaXplZAAAAAAAEAAAAB1Db250cmFjdCBub3QgeWV0IGluaXRpYWxpemVkLgAAAAAAAA5Ob3RJbml0aWFsaXplZAAAAAAAEQAAADhDYW5ub3QgY3JlYXRlIGEgcGVuZGluZyBzdHJlYW0gd2l0aCByYXRlX3Blcl9zZWNvbmQgPSAwLgAAABdDcmVhdGVSYXRlUGVyU2Vjb25kWmVybwAAAAASAAAAOUludGVybmFsIG1hdGggZXJyb3Ig4oCUIHNob3VsZCBuZXZlciBvY2N1ciBpbiBwcm9kdWN0aW9uLgAAAAAAABJJbnZhbGlkQ2FsY3VsYXRpb24AAAAAABMAAAAxU2VuZGVyIGFuZCByZWNpcGllbnQgbXVzdCBiZSBkaWZmZXJlbnQgYWRkcmVzc2VzLgAAAAAAABVTZW5kZXJFcXVhbHNSZWNpcGllbnQAAAAAAAAUAAAAJVJhdGUgcGVyIHNlY29uZCBtdXN0IG5vdCBiZSBuZWdhdGl2ZS4AAAAAAAAMTmVnYXRpdmVSYXRlAAAAFQAAAEVUb2tlbiB0cmFuc2ZlciByZXN1bHRlZCBpbiBhIGRpZmZlcmVudCBiYWxhbmNlIGNoYW5nZSB0aGFuIHJlcXVlc3RlZC4AAAAAAAAVVG9rZW5UcmFuc2Zlck1pc21hdGNoAAAAAAAAFgAAAD9DYWxsZXItc3VwcGxpZWQgdG9rZW4gZGVjaW1hbHMgZG8gbm90IG1hdGNoIHRoZSB0b2tlbiBjb250cmFjdC4AAAAAFVRva2VuRGVjaW1hbHNNaXNtYXRjaAAAAAAAABcAAAA5Q2hlY2tlZCBhcml0aG1ldGljIG9wZXJhdGlvbiBmYWlsZWQgKG92ZXJmbG93L3VuZGVyZmxvdykuAAAAAAAAD0FyaXRobWV0aWNFcnJvcgAAAAAYAAAAH0FkbWluIHRyYW5zZmVyIGFscmVhZHkgcGVuZGluZy4AAAAAFEFkbWluVHJhbnNmZXJQZW5kaW5nAAAAGQAAACRObyBhZG1pbiB0cmFuc2ZlciBwZW5kaW5nIHRvIGFjY2VwdC4AAAAWTm9BZG1pblRyYW5zZmVyUGVuZGluZwAAAAAAGgAAADFVcGdyYWRlIGlzIHRpbWVsb2NrZWQgYW5kIGNhbm5vdCBiZSBleGVjdXRlZCB5ZXQuAAAAAAAAEVVwZ3JhZGVUaW1lbG9ja2VkAAAAAAAAGwAAAB1ObyB1cGdyYWRlIGhhcyBiZWVuIHByb3Bvc2VkLgAAAAAAABFOb1VwZ3JhZGVQcm9wb3NlZAAAAAAAABw=", + "AAAABAAAAC5FcnJvcnMgZW1pdHRlZCBieSB0aGUgTG9ja3VwIHZlc3RpbmcgY29udHJhY3QuAAAAAAAAAAAAC0xvY2t1cEVycm9yAAAAABQAAAAZU3RyZWFtIElEIGRvZXMgbm90IGV4aXN0LgAAAAAAAA5TdHJlYW1Ob3RGb3VuZAAAAAAAZQAAABlDYWxsZXIgaXMgbm90IGF1dGhvcml6ZWQuAAAAAAAADFVuYXV0aG9yaXplZAAAAGYAAAAcU3RyZWFtIGlzIGFscmVhZHkgY2FuY2VsbGVkLgAAABBBbHJlYWR5Q2FuY2VsbGVkAAAAZwAAABlTdHJlYW0gaXMgbm90IGNhbmNlbGFibGUuAAAAAAAADU5vdENhbmNlbGFibGUAAAAAAABoAAAAKVdpdGhkcmF3IGFtb3VudCBleGNlZWRzIHVubG9ja2VkIGJhbGFuY2UuAAAAAAAACE92ZXJkcmF3AAAAaQAAADxJbnZhbGlkIHRpbWUgcGFyYW1ldGVycyAoc3RhcnQgPj0gZW5kLCBjbGlmZiBvdXRzaWRlIHJhbmdlKS4AAAAQSW52YWxpZFRpbWVSYW5nZQAAAGoAAAAZVG90YWwgYW1vdW50IG11c3QgYmUgPiAwLgAAAAAAAApBbW91bnRaZXJvAAAAAABrAAAAHUNvbnRyYWN0IGFscmVhZHkgaW5pdGlhbGl6ZWQuAAAAAAAAEkFscmVhZHlJbml0aWFsaXplZAAAAAAAbAAAAB1Db250cmFjdCBub3QgeWV0IGluaXRpYWxpemVkLgAAAAAAAA5Ob3RJbml0aWFsaXplZAAAAAAAbQAAADFTZW5kZXIgYW5kIHJlY2lwaWVudCBtdXN0IGJlIGRpZmZlcmVudCBhZGRyZXNzZXMuAAAAAAAAFVNlbmRlckVxdWFsc1JlY2lwaWVudAAAAAAAAG4AAAA3c3RhcnRfdW5sb2NrX2Ftb3VudCBvciBjbGlmZl91bmxvY2tfYW1vdW50IGlzIG5lZ2F0aXZlLgAAAAAUTmVnYXRpdmVVbmxvY2tBbW91bnQAAABvAAAALkNoZWNrZWQgYWRkaXRpb24gb2YgdW5sb2NrIGFtb3VudHMgb3ZlcmZsb3dlZC4AAAAAABFVbmxvY2tTdW1PdmVyZmxvdwAAAAAAAHAAAAA3VW5sb2NrIHN1bSBpcyBub3QgaW4gdGhlIHZhbGlkIHJhbmdlIFswLCB0b3RhbF9hbW91bnRdLgAAAAAQSW52YWxpZFVubG9ja1N1bQAAAHEAAAAwRGVmZW5zaXZlIGNhbmNlbGxhdGlvbiBhbW91bnQgdmFsaWRhdGlvbiBmYWlsZWQuAAAAGUludmFsaWRDYW5jZWxsYXRpb25BbW91bnQAAAAAAAByAAAARVRva2VuIHRyYW5zZmVyIHJlc3VsdGVkIGluIGEgZGlmZmVyZW50IGJhbGFuY2UgY2hhbmdlIHRoYW4gcmVxdWVzdGVkLgAAAAAAABVUb2tlblRyYW5zZmVyTWlzbWF0Y2gAAAAAAABzAAAAOUNoZWNrZWQgYXJpdGhtZXRpYyBvcGVyYXRpb24gZmFpbGVkIChvdmVyZmxvdy91bmRlcmZsb3cpLgAAAAAAAA9Bcml0aG1ldGljRXJyb3IAAAAAdAAAAB9BZG1pbiB0cmFuc2ZlciBhbHJlYWR5IHBlbmRpbmcuAAAAABRBZG1pblRyYW5zZmVyUGVuZGluZwAAAHUAAAAkTm8gYWRtaW4gdHJhbnNmZXIgcGVuZGluZyB0byBhY2NlcHQuAAAAFk5vQWRtaW5UcmFuc2ZlclBlbmRpbmcAAAAAAHYAAAAxVXBncmFkZSBpcyB0aW1lbG9ja2VkIGFuZCBjYW5ub3QgYmUgZXhlY3V0ZWQgeWV0LgAAAAAAABFVcGdyYWRlVGltZWxvY2tlZAAAAAAAAHcAAAAdTm8gdXBncmFkZSBoYXMgYmVlbiBwcm9wb3NlZC4AAAAAAAARTm9VcGdyYWRlUHJvcG9zZWQAAAAAAAB4", + "AAAABAAAACdFcnJvcnMgc3BlY2lmaWMgdG8gdGhlIFJvdXRlciBjb250cmFjdC4AAAAAAAAAAAtSb3V0ZXJFcnJvcgAAAAAFAAAAAAAAABJBbHJlYWR5SW5pdGlhbGl6ZWQAAAAAAS0AAAAAAAAADk5vdEluaXRpYWxpemVkAAAAAAEuAAAAAAAAAA1Ob3RBdXRob3JpemVkAAAAAAABLwAAAAAAAAARSW52YWxpZFN0cmVhbVR5cGUAAAAAAAEwAAAANEEgcmVxdWlyZWQgY29udHJhY3QgYWRkcmVzcyBpcyBpbnZhbGlkICh6ZXJvL3Vuc2V0KS4AAAAWSW52YWxpZENvbnRyYWN0QWRkcmVzcwAAAAABMQ==", + "AAAAAgAAAKFTdG9yYWdlIGtleXMgZm9yIGNvbnRyYWN0IGRhdGEuCgpVc2luZyBhIHR5cGVkIGVudW0gcHJldmVudHMga2V5IGNvbGxpc2lvbnMgKFNLSUxMLm1kIMKnMykuCktleXMgYXJlIG5hbWVzcGFjZWQgYnkgdmFyaWFudCB0byBrZWVwIGRpZmZlcmVudCBkYXRhIHR5cGVzIHNlcGFyYXRlLgAAAAAAAAAAAAAHRGF0YUtleQAAAAAQAAAAAAAAACFBZG1pbiBhZGRyZXNzIChJbnN0YW5jZSBzdG9yYWdlKS4AAAAAAAAFQWRtaW4AAAAAAAAAAAAAKk5leHQgc3RyZWFtIElEIGNvdW50ZXIgKEluc3RhbmNlIHN0b3JhZ2UpLgAAAAAADE5leHRTdHJlYW1JZAAAAAEAAAA+QSBGbG93IHN0cmVhbSByZWNvcmQsIGtleWVkIGJ5IHN0cmVhbSBJRCAoUGVyc2lzdGVudCBzdG9yYWdlKS4AAAAAAApGbG93U3RyZWFtAAAAAAABAAAABgAAAAEAAABAQSBMb2NrdXAgc3RyZWFtIHJlY29yZCwga2V5ZWQgYnkgc3RyZWFtIElEIChQZXJzaXN0ZW50IHN0b3JhZ2UpLgAAAAxMb2NrdXBTdHJlYW0AAAABAAAABgAAAAEAAACTQWdncmVnYXRlIHRva2VuIGJhbGFuY2UgaGVsZCBieSB0aGUgY29udHJhY3QgZm9yIGEgZ2l2ZW4gdG9rZW4gYWRkcmVzcy4KVXNlZCBmb3Igc3VycGx1cyByZWNvdmVyeSAvIGFjY291bnRpbmcgcmVjb25jaWxpYXRpb24gKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAAABBBZ2dyZWdhdGVCYWxhbmNlAAAAAQAAABMAAAABAAAAOE5GVCB0b2tlbiBvd25lciwga2V5ZWQgYnkgdG9rZW4gSUQgKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAAClRva2VuT3duZXIAAAAAAAEAAAALAAAAAQAAAExORlQgc3RyZWFtIGRhdGEgbWFwcGluZyB0b2tlbiBJRCB0byBzdHJlYW0gdHlwZSBhbmQgSUQgKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAAD1Rva2VuU3RyZWFtRGF0YQAAAAABAAAACwAAAAEAAAA4TnVtYmVyIG9mIE5GVHMgb3duZWQgYnkgYW4gYWRkcmVzcyAoUGVyc2lzdGVudCBzdG9yYWdlKS4AAAAKTmZ0QmFsYW5jZQAAAAAAAQAAABMAAAABAAAAO1Rva2VuIG1ldGFkYXRhLCBlLmcuLCBuYW1lLCBzeW1ib2wsIFVSSSAoSW5zdGFuY2Ugc3RvcmFnZSkuAAAAAA1Ub2tlbk1ldGFkYXRhAAAAAAAAAQAAABEAAAAAAAAALFJvdXRlciBjb25maWd1cmF0aW9uOiBGbG93IGNvbnRyYWN0IGFkZHJlc3MuAAAADEZsb3dDb250cmFjdAAAAAAAAAAuUm91dGVyIGNvbmZpZ3VyYXRpb246IExvY2t1cCBjb250cmFjdCBhZGRyZXNzLgAAAAAADkxvY2t1cENvbnRyYWN0AAAAAAAAAAAAK1JvdXRlciBjb25maWd1cmF0aW9uOiBORlQgY29udHJhY3QgYWRkcmVzcy4AAAAAC05mdENvbnRyYWN0AAAAAAAAAAA9UGF5bWFzdGVyIGNvbmZpZ3VyYXRpb246IGxpc3Qgb2YgYWxsb3dlZCBmZWUgdG9rZW4gYWRkcmVzc2VzLgAAAAAAABBBbGxvd2VkRmVlVG9rZW5zAAAAAAAAAEVQZW5kaW5nIGFkbWluIGFkZHJlc3MgZm9yIHR3by1zdGVwIGFkbWluIHRyYW5zZmVyIChJbnN0YW5jZSBzdG9yYWdlKS4AAAAAAAAMUGVuZGluZ0FkbWluAAAAAQAAAC5Qcm9wb3NlZCB1cGdyYWRlIFdBU00gaGFzaCAoSW5zdGFuY2Ugc3RvcmFnZSkuAAAAAAAPUHJvcG9zZWRVcGdyYWRlAAAAAAEAAAPuAAAAIAAAAAAAAABSTGVkZ2VyIHNlcXVlbmNlIGF0IHdoaWNoIGEgcHJvcG9zZWQgdXBncmFkZSBiZWNvbWVzIGV4ZWN1dGFibGUgKEluc3RhbmNlIHN0b3JhZ2UpLgAAAAAAE1VwZ3JhZGVVbmxvY2tMZWRnZXIA" ]), options ) } public readonly fromJSON = { upgrade: this.txFromJSON, - owner_of: this.txFromJSON, withdraw: this.txFromJSON, - configure: this.txFromJSON, - set_admin: this.txFromJSON, - status_of: this.txFromJSON, - void_flow: this.txFromJSON, - get_stream: this.txFromJSON, - stream_type: this.txFromJSON, + initialize: this.txFromJSON, upgrade_nft: this.txFromJSON, + accept_admin: this.txFromJSON, withdraw_max: this.txFromJSON, - core_stream_id: this.txFromJSON, + propose_admin: this.txFromJSON, + execute_upgrade: this.txFromJSON, + propose_upgrade: this.txFromJSON, create_flow_stream: this.txFromJSON, create_lockup_stream: this.txFromJSON } diff --git a/src/generated/stream_nft/src/index.ts b/src/generated/stream_nft/src/index.ts index cc66fcb..419cf13 100644 --- a/src/generated/stream_nft/src/index.ts +++ b/src/generated/stream_nft/src/index.ts @@ -27,7 +27,7 @@ export * as rpc from "@stellar/stellar-sdk/rpc"; /** * Core data structure for a Flow (open-ended, rate-per-second) stream. - * + * * Ported from Sablier's `Flow.Stream` struct with these adaptations: * * - `UD21x18 ratePerSecond` → `i128 rate_per_second` scaled to 18 decimals. @@ -219,37 +219,6 @@ export enum StreamStatus { } -/** - * Stable public metadata for a Stream NFT and its underlying core stream. - */ -export interface StreamMetadata { - /** - * Internal identifier assigned by the selected core engine. - */ -core_stream_id: u64; - /** - * Current holder of withdrawal and NFT-owner Flow void rights. - */ -owner: string; - /** - * Canonical cross-engine lifecycle state. - */ -status: CanonicalStreamStatus; - /** - * Core engine kind. - */ -stream_type: StreamType; - /** - * Public, deployment-scoped stream identifier. - */ -token_id: i128; - /** - * Immutable transferability policy selected when the stream is created. - */ -transferable: boolean; -} - - /** * Parameters for creating a new Lockup stream. * @@ -303,21 +272,6 @@ token: string; total_amount: i128; } -/** - * Canonical lifecycle exposed by the Router to every product surface. - * - * Core engines retain their more detailed accounting states; the Router maps - * those states into this stable cross-engine vocabulary. - */ -export enum CanonicalStreamStatus { - Pending = 0, - Active = 1, - Paused = 2, - Canceled = 3, - Completed = 4, - Failed = 5, -} - /** * Errors specific to the Stream NFT contract. */ @@ -421,9 +375,33 @@ export const FlowError = { */ 21: {message:"NegativeRate"}, /** - * The token did not debit and credit the exact requested amount. + * Token transfer resulted in a different balance change than requested. */ - 22: {message:"TokenTransferMismatch"} + 22: {message:"TokenTransferMismatch"}, + /** + * Caller-supplied token decimals do not match the token contract. + */ + 23: {message:"TokenDecimalsMismatch"}, + /** + * Checked arithmetic operation failed (overflow/underflow). + */ + 24: {message:"ArithmeticError"}, + /** + * Admin transfer already pending. + */ + 25: {message:"AdminTransferPending"}, + /** + * No admin transfer pending to accept. + */ + 26: {message:"NoAdminTransferPending"}, + /** + * Upgrade is timelocked and cannot be executed yet. + */ + 27: {message:"UpgradeTimelocked"}, + /** + * No upgrade has been proposed. + */ + 28: {message:"NoUpgradeProposed"} } /** @@ -471,13 +449,45 @@ export const LockupError = { */ 110: {message:"SenderEqualsRecipient"}, /** - * Start and cliff unlock amounts must be nonnegative and fit in total amount. + * start_unlock_amount or cliff_unlock_amount is negative. + */ + 111: {message:"NegativeUnlockAmount"}, + /** + * Checked addition of unlock amounts overflowed. + */ + 112: {message:"UnlockSumOverflow"}, + /** + * Unlock sum is not in the valid range [0, total_amount]. + */ + 113: {message:"InvalidUnlockSum"}, + /** + * Defensive cancellation amount validation failed. + */ + 114: {message:"InvalidCancellationAmount"}, + /** + * Token transfer resulted in a different balance change than requested. + */ + 115: {message:"TokenTransferMismatch"}, + /** + * Checked arithmetic operation failed (overflow/underflow). */ - 111: {message:"InvalidUnlockAmount"}, + 116: {message:"ArithmeticError"}, /** - * The token did not debit and credit the exact requested amount. + * Admin transfer already pending. */ - 112: {message:"TokenTransferMismatch"} + 117: {message:"AdminTransferPending"}, + /** + * No admin transfer pending to accept. + */ + 118: {message:"NoAdminTransferPending"}, + /** + * Upgrade is timelocked and cannot be executed yet. + */ + 119: {message:"UpgradeTimelocked"}, + /** + * No upgrade has been proposed. + */ + 120: {message:"NoUpgradeProposed"} } /** @@ -489,9 +499,9 @@ export const RouterError = { 303: {message:"NotAuthorized"}, 304: {message:"InvalidStreamType"}, /** - * Core contract addresses were already configured. + * A required contract address is invalid (zero/unset). */ - 305: {message:"AlreadyConfigured"} + 305: {message:"InvalidContractAddress"} } /** @@ -500,15 +510,22 @@ export const RouterError = { * Using a typed enum prevents key collisions (SKILL.md §3). * Keys are namespaced by variant to keep different data types separate. */ -export type DataKey = {tag: "Admin", values: void} | {tag: "NextStreamId", values: void} | {tag: "FlowStream", values: readonly [u64]} | {tag: "LockupStream", values: readonly [u64]} | {tag: "AggregateBalance", values: readonly [string]} | {tag: "TokenOwner", values: readonly [i128]} | {tag: "TokenStreamData", values: readonly [i128]} | {tag: "TokenTransferable", values: readonly [i128]} | {tag: "NftBalance", values: readonly [string]} | {tag: "TokenMetadata", values: readonly [string]} | {tag: "FlowContract", values: void} | {tag: "LockupContract", values: void} | {tag: "NftContract", values: void} | {tag: "Router", values: void} | {tag: "AllowedFeeTokens", values: void}; +export type DataKey = {tag: "Admin", values: void} | {tag: "NextStreamId", values: void} | {tag: "FlowStream", values: readonly [u64]} | {tag: "LockupStream", values: readonly [u64]} | {tag: "AggregateBalance", values: readonly [string]} | {tag: "TokenOwner", values: readonly [i128]} | {tag: "TokenStreamData", values: readonly [i128]} | {tag: "NftBalance", values: readonly [string]} | {tag: "TokenMetadata", values: readonly [string]} | {tag: "FlowContract", values: void} | {tag: "LockupContract", values: void} | {tag: "NftContract", values: void} | {tag: "AllowedFeeTokens", values: void} | {tag: "PendingAdmin", values: void} | {tag: "ProposedUpgrade", values: readonly [Buffer]} | {tag: "UpgradeUnlockLedger", values: void}; export interface Client { + /** + * Construct and simulate a burn transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Burn an NFT (e.g. when stream is depleted or voided). + * Only admin (Router) can burn. + */ + burn: ({token_id}: {token_id: i128}, options?: MethodOptions) => Promise> + /** * Construct and simulate a mint transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. * Mint a new NFT representing a stream. * Only the admin (Router) can mint. */ - mint: ({to, stream_type, stream_id, token_id, transferable}: {to: string, stream_type: StreamType, stream_id: u64, token_id: i128, transferable: boolean}, options?: MethodOptions) => Promise> + mint: ({to, stream_type, stream_id, token_id}: {to: string, stream_type: StreamType, stream_id: u64, token_id: i128}, options?: MethodOptions) => Promise> /** * Construct and simulate a name transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. @@ -547,22 +564,22 @@ export interface Client { transfer: ({from, to, token_id}: {from: string, to: string, token_id: i128}, options?: MethodOptions) => Promise> /** - * Construct and simulate a get_stream_data transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Get the stream details associated with an NFT. + * Construct and simulate a initialize transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Initialize the NFT contract with the Router contract address as admin. + * + * Note: admin is the Router contract address, which cannot authorize off-chain transactions. */ - get_stream_data: ({token_id}: {token_id: i128}, options?: MethodOptions) => Promise> + initialize: ({admin, name, symbol}: {admin: string, name: string, symbol: string}, options?: MethodOptions) => Promise> /** - * Construct and simulate a is_transferable transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Return the immutable per-stream transferability policy. + * Construct and simulate a get_stream_data transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Get the stream details associated with an NFT. */ - is_transferable: ({token_id}: {token_id: i128}, options?: MethodOptions) => Promise> + get_stream_data: ({token_id}: {token_id: i128}, options?: MethodOptions) => Promise> } export class Client extends ContractClient { static async deploy( - /** Constructor/Initialization Args for the contract's `__constructor` method */ - {admin, name, symbol}: {admin: string, name: string, symbol: string}, /** Options for initializing a Client as well as for calling a method, with extras specific to deploying. */ options: MethodOptions & Omit & { @@ -574,45 +591,44 @@ export class Client extends ContractClient { format?: "hex" | "base64"; } ): Promise> { - return ContractClient.deploy({admin, name, symbol}, options) + return ContractClient.deploy(null, options) } constructor(public readonly options: ContractClientOptions) { super( - new ContractSpec([ "AAAAAAAAAEdNaW50IGEgbmV3IE5GVCByZXByZXNlbnRpbmcgYSBzdHJlYW0uCk9ubHkgdGhlIGFkbWluIChSb3V0ZXIpIGNhbiBtaW50LgAAAAAEbWludAAAAAUAAAAAAAAAAnRvAAAAAAATAAAAAAAAAAtzdHJlYW1fdHlwZQAAAAfQAAAAClN0cmVhbVR5cGUAAAAAAAAAAAAJc3RyZWFtX2lkAAAAAAAABgAAAAAAAAAIdG9rZW5faWQAAAALAAAAAAAAAAx0cmFuc2ZlcmFibGUAAAABAAAAAA==", + new ContractSpec([ "AAAAAAAAAFNCdXJuIGFuIE5GVCAoZS5nLiB3aGVuIHN0cmVhbSBpcyBkZXBsZXRlZCBvciB2b2lkZWQpLgpPbmx5IGFkbWluIChSb3V0ZXIpIGNhbiBidXJuLgAAAAAEYnVybgAAAAEAAAAAAAAACHRva2VuX2lkAAAACwAAAAA=", + "AAAAAAAAAEdNaW50IGEgbmV3IE5GVCByZXByZXNlbnRpbmcgYSBzdHJlYW0uCk9ubHkgdGhlIGFkbWluIChSb3V0ZXIpIGNhbiBtaW50LgAAAAAEbWludAAAAAQAAAAAAAAAAnRvAAAAAAATAAAAAAAAAAtzdHJlYW1fdHlwZQAAAAfQAAAAClN0cmVhbVR5cGUAAAAAAAAAAAAJc3RyZWFtX2lkAAAAAAAABgAAAAAAAAAIdG9rZW5faWQAAAALAAAAAA==", "AAAAAAAAAA5HZXQgdG9rZW4gbmFtZQAAAAAABG5hbWUAAAAAAAAAAQAAABA=", "AAAAAAAAABBHZXQgdG9rZW4gc3ltYm9sAAAABnN5bWJvbAAAAAAAAAAAAAEAAAAQ", "AAAAAAAAACtHZXQgdGhlIG51bWJlciBvZiBORlRzIG93bmVkIGJ5IGFuIGFkZHJlc3MuAAAAAAdiYWxhbmNlAAAAAAEAAAAAAAAABW93bmVyAAAAAAAAEwAAAAEAAAAL", "AAAAAAAAACVBZG1pbiBjYW4gdXBncmFkZSB0aGUgY29udHJhY3QgbG9naWMuAAAAAAAAB3VwZ3JhZGUAAAAAAQAAAAAAAAANbmV3X3dhc21faGFzaAAAAAAAA+4AAAAgAAAAAA==", "AAAAAAAAABhHZXQgdGhlIG93bmVyIG9mIGFuIE5GVC4AAAAIb3duZXJfb2YAAAABAAAAAAAAAAh0b2tlbl9pZAAAAAsAAAABAAAAEw==", "AAAAAAAAAB9UcmFuc2ZlciBhbiBORlQgdG8gYSBuZXcgb3duZXIuAAAAAAh0cmFuc2ZlcgAAAAMAAAAAAAAABGZyb20AAAATAAAAAAAAAAJ0bwAAAAAAEwAAAAAAAAAIdG9rZW5faWQAAAALAAAAAA==", - "AAAAAAAAADlBdG9taWNhbGx5IGluaXRpYWxpemUgdGhlIE5GVCBjb250cmFjdCBkdXJpbmcgZGVwbG95bWVudC4AAAAAAAANX19jb25zdHJ1Y3RvcgAAAAAAAAMAAAAAAAAABWFkbWluAAAAAAAAEwAAAAAAAAAEbmFtZQAAABAAAAAAAAAABnN5bWJvbAAAAAAAEAAAAAA=", + "AAAAAAAAAKJJbml0aWFsaXplIHRoZSBORlQgY29udHJhY3Qgd2l0aCB0aGUgUm91dGVyIGNvbnRyYWN0IGFkZHJlc3MgYXMgYWRtaW4uCgpOb3RlOiBhZG1pbiBpcyB0aGUgUm91dGVyIGNvbnRyYWN0IGFkZHJlc3MsIHdoaWNoIGNhbm5vdCBhdXRob3JpemUgb2ZmLWNoYWluIHRyYW5zYWN0aW9ucy4AAAAAAAppbml0aWFsaXplAAAAAAADAAAAAAAAAAVhZG1pbgAAAAAAABMAAAAAAAAABG5hbWUAAAAQAAAAAAAAAAZzeW1ib2wAAAAAABAAAAAA", "AAAAAAAAAC5HZXQgdGhlIHN0cmVhbSBkZXRhaWxzIGFzc29jaWF0ZWQgd2l0aCBhbiBORlQuAAAAAAAPZ2V0X3N0cmVhbV9kYXRhAAAAAAEAAAAAAAAACHRva2VuX2lkAAAACwAAAAEAAAPtAAAAAgAAB9AAAAAKU3RyZWFtVHlwZQAAAAAABg==", - "AAAAAAAAADdSZXR1cm4gdGhlIGltbXV0YWJsZSBwZXItc3RyZWFtIHRyYW5zZmVyYWJpbGl0eSBwb2xpY3kuAAAAAA9pc190cmFuc2ZlcmFibGUAAAAAAQAAAAAAAAAIdG9rZW5faWQAAAALAAAAAQAAAAE=", "AAAAAQAAA3pDb3JlIGRhdGEgc3RydWN0dXJlIGZvciBhIEZsb3cgKG9wZW4tZW5kZWQsIHJhdGUtcGVyLXNlY29uZCkgc3RyZWFtLgoKUG9ydGVkIGZyb20gU2FibGllcidzIGBGbG93LlN0cmVhbWAgc3RydWN0IHdpdGggdGhlc2UgYWRhcHRhdGlvbnM6CgotIGBVRDIxeDE4IHJhdGVQZXJTZWNvbmRgIOKGkiBgaTEyOCByYXRlX3Blcl9zZWNvbmRgIHNjYWxlZCB0byAxOCBkZWNpbWFscy4KU29yb2JhbiBoYXMgbm8gbmF0aXZlIGZpeGVkLXBvaW50IHR5cGUsIHNvIHdlIHVzZSBpMTI4IHdpdGggbWFudWFsIHNjYWxpbmcuCkEgcmF0ZSBvZiAxIHRva2VuL3NlYyBmb3IgYSA3LWRlY2ltYWwgdG9rZW4gPSAxZTE4IGludGVybmFsbHkuCgotIGBJRVJDMjAgdG9rZW5gIOKGkiBgQWRkcmVzc2AgKFNvcm9iYW4gdG9rZW4gY29udHJhY3QgYWRkcmVzcykuCgotIGB1aW50MTI4IGJhbGFuY2VgIOKGkiBgaTEyOGAgKFNvcm9iYW4gU0RLIGNvbnZlbnRpb24gZm9yIHRva2VuIGFtb3VudHMpLgoKLSBgdWludDQwIHNuYXBzaG90VGltZWAg4oaSIGB1NjRgIChTb3JvYmFuIGxlZGdlciB0aW1lc3RhbXAgaXMgdTY0KS4KCi0gYGJvb2wgaXNTdHJlYW1gIHNlbnRpbmVsIHJlbW92ZWQg4oCUIHdlIHVzZSBzdG9yYWdlIGtleSBleGlzdGVuY2UgaW5zdGVhZC4KCi0gYGJvb2wgaXNUcmFuc2ZlcmFibGVgIHJlbW92ZWQg4oCUIGRlbGVnYXRlZCB0byB0aGUgTkZUIGNvbnRyYWN0IGxheWVyLgoKIyBTdG9yYWdlIExheW91dAoKRWFjaCBzdHJlYW0gaXMgc3RvcmVkIHVuZGVyIGBEYXRhS2V5OjpTdHJlYW0oc3RyZWFtX2lkKWAgaW4gcGVyc2lzdGVudCBzdG9yYWdlLgpUaGUgc3RydWN0IGlzIGtlcHQgYXMgZmxhdCBhcyBwb3NzaWJsZSB0byBtaW5pbWl6ZSBzZXJpYWxpemF0aW9uIG92ZXJoZWFkLgAAAAAAAAAAAApGbG93U3RyZWFtAAAAAAAJAAAATkN1cnJlbnQgYmFsYW5jZSBoZWxkIGluIHRoZSBzdHJlYW0gKGRlcG9zaXRlZCAtIHdpdGhkcmF3biksIGluIHRva2VuIGRlY2ltYWxzLgAAAAAAB2JhbGFuY2UAAAAACwAAAC9XaGV0aGVyIHRoZSBzdHJlYW0gaGFzIGJlZW4gcGVybWFuZW50bHkgdm9pZGVkLgAAAAAJaXNfdm9pZGVkAAAAAAAAAQAAAKVSYXRlIGF0IHdoaWNoIGRlYnQgYWNjcnVlcywgaW4gMTgtZGVjaW1hbCBmaXhlZC1wb2ludC4KMCBtZWFucyB0aGUgc3RyZWFtIGlzIHBhdXNlZC4KRXhhbXBsZTogMSB0b2tlbi9zZWMgZm9yIGEgNy1kZWNpbWFsIHRva2VuIOKGkiAxXzAwMF8wMDBfMDAwXzAwMF8wMDBfMDAwICgxZTE4KS4AAAAAAAAPcmF0ZV9wZXJfc2Vjb25kAAAAAAsAAAAwVGhlIGFkZHJlc3MgcmVjZWl2aW5nIHRoZSB0b2tlbnMgKGNhbiB3aXRoZHJhdykuAAAACXJlY2lwaWVudAAAAAAAABMAAAA9VGhlIGFkZHJlc3Mgc3RyZWFtaW5nIHRoZSB0b2tlbnMgKGNhbiBwYXVzZSwgYWRqdXN0LCByZWZ1bmQpLgAAAAAAAAZzZW5kZXIAAAAAABMAAAB6QWNjdW11bGF0ZWQgZGVidCBhdCB0aGUgbGFzdCBzbmFwc2hvdCwgaW4gMTgtZGVjaW1hbCBmaXhlZC1wb2ludC4KVG90YWwgZGVidCA9IHNuYXBzaG90X2RlYnRfc2NhbGVkICsgb25nb2luZ19kZWJ0X3NjYWxlZC4AAAAAABRzbmFwc2hvdF9kZWJ0X3NjYWxlZAAAAAsAAABkVW5peCB0aW1lc3RhbXAgb2YgdGhlIGxhc3QgZGVidCBzbmFwc2hvdC4KRGVidCBhY2NydWVzIGZyb20gdGhpcyBwb2ludCBmb3J3YXJkIGF0IGByYXRlX3Blcl9zZWNvbmRgLgAAAA1zbmFwc2hvdF90aW1lAAAAAAAABgAAADpUaGUgU29yb2JhbiB0b2tlbiBjb250cmFjdCBhZGRyZXNzIChTQUMgb3IgY3VzdG9tIFNFUC00MSkuAAAAAAAFdG9rZW4AAAAAAAATAAAAbU51bWJlciBvZiBkZWNpbWFscyBmb3IgdGhlIHRva2VuIChlLmcuIDcgZm9yIG1vc3QgU3RlbGxhciBhc3NldHMpLgpVc2VkIGZvciBzY2FsZS9kZXNjYWxlIG9wZXJhdGlvbnMuIE1heCAxOC4AAAAAAAAOdG9rZW5fZGVjaW1hbHMAAAAAAAQ=", "AAAAAwAAAGhUeXBlIG9mIHN0cmVhbSAoRmxvdyBvciBMb2NrdXApLgpVc2VkIGJ5IHRoZSBTdHJlYW0gTkZUIGFuZCBSb3V0ZXIgdG8gcm91dGUgbG9naWMgdG8gdGhlIGNvcnJlY3QgZW5naW5lLgAAAAAAAAAKU3RyZWFtVHlwZQAAAAAAAgAAAAAAAAAERmxvdwAAAAAAAAAAAAAABkxvY2t1cAAAAAAAAQ==", "AAAAAwAAAMtTdGF0dXMgb2YgYSBMb2NrdXAgc3RyZWFtLgoKVXNlcyAidGVtcGVyYXR1cmUiIHNlbWFudGljczoKLSAqKldhcm0qKiAoUGVuZGluZywgU3RyZWFtaW5nKTogdGltZSBhbG9uZSBjYW4gY2hhbmdlIHRoZSBzdGF0dXMuCi0gKipDb2xkKiogKFNldHRsZWQsIENhbmNlbGVkLCBEZXBsZXRlZCk6IHRpbWUgYWxvbmUgY2Fubm90IGNoYW5nZSB0aGUgc3RhdHVzLgAAAAAAAAAADExvY2t1cFN0YXR1cwAAAAUAAAA/Q3JlYXRlZCBidXQgc3RhcnRfdGltZSBpcyBpbiB0aGUgZnV0dXJlLiBObyB0b2tlbnMgaGF2ZSB2ZXN0ZWQuAAAAAAdQZW5kaW5nAAAAAAAAAAAyQWN0aXZlIOKAlCB0b2tlbnMgYXJlIGN1cnJlbnRseSB2ZXN0aW5nIG92ZXIgdGltZS4AAAAAAAlTdHJlYW1pbmcAAAAAAAABAAAAS0FsbCB0b2tlbnMgaGF2ZSBmdWxseSB2ZXN0ZWQuIFJlY2lwaWVudCBjYW4gd2l0aGRyYXcgdGhlIHJlbWFpbmluZyBiYWxhbmNlLgAAAAAHU2V0dGxlZAAAAAACAAAAP1NlbmRlciBjYW5jZWxlZCB0aGUgc3RyZWFtLiBVbnZlc3RlZCB0b2tlbnMgcmV0dXJuZWQgdG8gc2VuZGVyLgAAAAAIQ2FuY2VsZWQAAAADAAAAQkZ1bGx5IHdpdGhkcmF3biAoYW5kL29yIHJlZnVuZGVkKS4gTm8gdG9rZW5zIHJlbWFpbiBpbiB0aGUgc3RyZWFtLgAAAAAACERlcGxldGVkAAAABA==", "AAAAAQAAAtxDb3JlIGRhdGEgc3RydWN0dXJlIGZvciBhIExvY2t1cCAoZml4ZWQtdGVybSB2ZXN0aW5nKSBzdHJlYW0uCgpTdXBwb3J0cyBsaW5lYXIgdW5sb2NrIHdpdGggb3B0aW9uYWwgY2xpZmYuIFRoZSB2ZXN0ZWQgKCJzdHJlYW1lZCIpIGFtb3VudAphdCBhbnkgdGltZSBgdGAgaXMgY2FsY3VsYXRlZCBhczoKCmBgYHRleHQKaWYgdCA8IHN0YXJ0X3RpbWU6ICAgICAgIHZlc3RlZCA9IDAKaWYgdCA8IGNsaWZmX3RpbWU6ICAgICAgIHZlc3RlZCA9IHN0YXJ0X3VubG9ja19hbW91bnQKaWYgdCA+PSBlbmRfdGltZTogICAgICAgICB2ZXN0ZWQgPSB0b3RhbF9hbW91bnQKZWxzZToKZWxhcHNlZCA9IGZsb29yKCh0IC0gY2xpZmZfdGltZSkgLyBncmFudWxhcml0eSkgKiBncmFudWxhcml0eQpzdHJlYW1hYmxlX2R1cmF0aW9uID0gZW5kX3RpbWUgLSBjbGlmZl90aW1lCnN0cmVhbWFibGVfYW1vdW50ID0gdG90YWxfYW1vdW50IC0gc3RhcnRfdW5sb2NrX2Ftb3VudCAtIGNsaWZmX3VubG9ja19hbW91bnQKdmVzdGVkID0gc3RhcnRfdW5sb2NrX2Ftb3VudCArIGNsaWZmX3VubG9ja19hbW91bnQgKyAoZWxhcHNlZCAqIHN0cmVhbWFibGVfYW1vdW50IC8gc3RyZWFtYWJsZV9kdXJhdGlvbikKYGBgCgpUaGlzIG1pcnJvcnMgdGhlIHJlZmVyZW5jZSBsaW5lYXIgbG9ja3VwIGNhbGN1bGF0aW9uIHdpdGggZGlzY3JldGUgdW5sb2NrCnN0ZXBzIGF0IGBncmFudWxhcml0eWAtc2Vjb25kIGludGVydmFscy4AAAAAAAAADExvY2t1cFN0cmVhbQAAAA8AAABGV2hldGhlciB0aGUgc2VuZGVyIGNhbiBjYW5jZWwgdGhpcyBzdHJlYW0gYW5kIHJlY2xhaW0gdW52ZXN0ZWQgdG9rZW5zLgAAAAAACmNhbmNlbGFibGUAAAAAAAEAAABuT3B0aW9uYWwgY2xpZmYgdGltZXN0YW1wLiBObyB0b2tlbnMgYmV5b25kIGBzdGFydF91bmxvY2tfYW1vdW50YAp2ZXN0IGJlZm9yZSB0aGlzIHRpbWUuIFNldCB0byAwIGZvciBubyBjbGlmZi4AAAAAAApjbGlmZl90aW1lAAAAAAAGAAAAR0Ftb3VudCB1bmxvY2tlZCBhdCBgY2xpZmZfdGltZWAgKGluIGFkZGl0aW9uIHRvIGBzdGFydF91bmxvY2tfYW1vdW50YCkuAAAAABNjbGlmZl91bmxvY2tfYW1vdW50AAAAAAsAAAAxVW5peCB0aW1lc3RhbXAgd2hlbiB0aGUgbG9ja3VwIGlzIGZ1bGx5IHVubG9ja2VkLgAAAAAAAAhlbmRfdGltZQAAAAYAAAB9VW5sb2NrIGdyYW51bGFyaXR5IGluIHNlY29uZHMuIFRva2VucyB2ZXN0IGluIGRpc2NyZXRlIHN0ZXBzIG9mIHRoaXMKaW50ZXJ2YWwuIERlZmF1bHQgPSAxIChwZXItc2Vjb25kIHZlc3RpbmcpLiBNdXN0IGJlID4gMC4AAAAAAAALZ3JhbnVsYXJpdHkAAAAABgAAADdXaGV0aGVyIGFsbCB0b2tlbnMgaGF2ZSBiZWVuIHdpdGhkcmF3biBhbmQvb3IgcmVmdW5kZWQuAAAAAAtpc19kZXBsZXRlZAAAAAABAAAAP1RoZSBhZGRyZXNzIHRoYXQgcmVjZWl2ZXMgdG9rZW5zIGFzIHRoZXkgdW5sb2NrIChjYW4gd2l0aGRyYXcpLgAAAAAJcmVjaXBpZW50AAAAAAAAEwAAAEFBbW91bnQgcmVmdW5kZWQgdG8gc2VuZGVyIG9uIGNhbmNlbGxhdGlvbi4gWmVybyB1bmxlc3MgY2FuY2VsbGVkLgAAAAAAAA9yZWZ1bmRlZF9hbW91bnQAAAAACwAAAExUaGUgYWRkcmVzcyB0aGF0IGNyZWF0ZWQgYW5kIGZ1bmRlZCB0aGUgbG9ja3VwIChjYW4gY2FuY2VsIGlmIGBjYW5jZWxhYmxlYCkuAAAABnNlbmRlcgAAAAAAEwAAACZVbml4IHRpbWVzdGFtcCB3aGVuIHRoZSBsb2NrdXAgYmVnaW5zLgAAAAAACnN0YXJ0X3RpbWUAAAAAAAYAAAAsQW1vdW50IHVubG9ja2VkIGltbWVkaWF0ZWx5IGF0IGBzdGFydF90aW1lYC4AAAATc3RhcnRfdW5sb2NrX2Ftb3VudAAAAAALAAAAI1RoZSBTb3JvYmFuIHRva2VuIGNvbnRyYWN0IGFkZHJlc3MuAAAAAAV0b2tlbgAAAAAAABMAAAA7VG90YWwgYW1vdW50IGRlcG9zaXRlZCBpbnRvIHRoZSBzdHJlYW0gKGluIHRva2VuIGRlY2ltYWxzKS4AAAAADHRvdGFsX2Ftb3VudAAAAAsAAAAmV2hldGhlciB0aGUgc3RyZWFtIGhhcyBiZWVuIGNhbmNlbGxlZC4AAAAAAAx3YXNfY2FuY2VsZWQAAAABAAAALUN1bXVsYXRpdmUgYW1vdW50IHdpdGhkcmF3biBieSB0aGUgcmVjaXBpZW50LgAAAAAAABB3aXRoZHJhd25fYW1vdW50AAAACw==", "AAAAAwAAALFTdGF0dXMgb2YgYSBGbG93IHN0cmVhbS4KClRoZSBzdGF0dXMgaXMgZGVyaXZlZCBmcm9tIHRoZSBzdHJlYW0ncyBjdXJyZW50IHN0YXRlIChyYXRlLCBiYWxhbmNlLCBkZWJ0LCB2b2lkZWQgZmxhZykgcmF0aGVyIHRoYW4KYmVpbmcgc3RvcmVkIGRpcmVjdGx5IOKAlCBrZWVwaW5nIHN0b3JhZ2UgbWluaW1hbC4AAAAAAAAAAAAADFN0cmVhbVN0YXR1cwAAAAYAAAA+U3RyZWFtIHNjaGVkdWxlZCB0byBzdGFydCBpbiB0aGUgZnV0dXJlIChzbmFwc2hvdF90aW1lID4gbm93KS4AAAAAAAdQZW5kaW5nAAAAAAAAAAAyQWN0aXZlbHkgc3RyZWFtaW5nIHdpdGggYmFsYW5jZSBjb3ZlcmluZyBhbGwgZGVidC4AAAAAABBTdHJlYW1pbmdTb2x2ZW50AAAAAQAAADZBY3RpdmVseSBzdHJlYW1pbmcgYnV0IGRlYnQgZXhjZWVkcyBhdmFpbGFibGUgYmFsYW5jZS4AAAAAABJTdHJlYW1pbmdJbnNvbHZlbnQAAAAAAAIAAAAoUGF1c2VkIGJ5IHNlbmRlciB3aXRoIG5vIHVuY292ZXJlZCBkZWJ0LgAAAA1QYXVzZWRTb2x2ZW50AAAAAAAAAwAAACVQYXVzZWQgYnkgc2VuZGVyIHdpdGggdW5jb3ZlcmVkIGRlYnQuAAAAAAAAD1BhdXNlZEluc29sdmVudAAAAAAEAAAARVBlcm1hbmVudGx5IHN0b3BwZWQuIENhbm5vdCBiZSByZXN0YXJ0ZWQuIFVuY292ZXJlZCBkZWJ0IHdyaXR0ZW4gb2ZmLgAAAAAAAAZWb2lkZWQAAAAAAAU=", - "AAAAAQAAAEdTdGFibGUgcHVibGljIG1ldGFkYXRhIGZvciBhIFN0cmVhbSBORlQgYW5kIGl0cyB1bmRlcmx5aW5nIGNvcmUgc3RyZWFtLgAAAAAAAAAADlN0cmVhbU1ldGFkYXRhAAAAAAAGAAAAOUludGVybmFsIGlkZW50aWZpZXIgYXNzaWduZWQgYnkgdGhlIHNlbGVjdGVkIGNvcmUgZW5naW5lLgAAAAAAAA5jb3JlX3N0cmVhbV9pZAAAAAAABgAAADxDdXJyZW50IGhvbGRlciBvZiB3aXRoZHJhd2FsIGFuZCBORlQtb3duZXIgRmxvdyB2b2lkIHJpZ2h0cy4AAAAFb3duZXIAAAAAAAATAAAAJ0Nhbm9uaWNhbCBjcm9zcy1lbmdpbmUgbGlmZWN5Y2xlIHN0YXRlLgAAAAAGc3RhdHVzAAAAAAfQAAAAFUNhbm9uaWNhbFN0cmVhbVN0YXR1cwAAAAAAABFDb3JlIGVuZ2luZSBraW5kLgAAAAAAAAtzdHJlYW1fdHlwZQAAAAfQAAAAClN0cmVhbVR5cGUAAAAAACxQdWJsaWMsIGRlcGxveW1lbnQtc2NvcGVkIHN0cmVhbSBpZGVudGlmaWVyLgAAAAh0b2tlbl9pZAAAAAsAAABFSW1tdXRhYmxlIHRyYW5zZmVyYWJpbGl0eSBwb2xpY3kgc2VsZWN0ZWQgd2hlbiB0aGUgc3RyZWFtIGlzIGNyZWF0ZWQuAAAAAAAADHRyYW5zZmVyYWJsZQAAAAE=", "AAAAAQAAAINQYXJhbWV0ZXJzIGZvciBjcmVhdGluZyBhIG5ldyBMb2NrdXAgc3RyZWFtLgoKQnVuZGxlZCBpbnRvIGEgc3RydWN0IGJlY2F1c2UgU29yb2JhbiBjb250cmFjdCBmdW5jdGlvbnMKaGF2ZSBhIG1heCBvZiAxMCBwYXJhbWV0ZXJzLgAAAAAAAAAAEkNyZWF0ZUxvY2t1cFBhcmFtcwAAAAAACwAAAClXaGV0aGVyIHRoZSBzZW5kZXIgY2FuIGNhbmNlbCB0aGUgc3RyZWFtLgAAAAAAAApjYW5jZWxhYmxlAAAAAAABAAAAME9wdGlvbmFsIGNsaWZmIHRpbWVzdGFtcC4gU2V0IHRvIDAgZm9yIG5vIGNsaWZmLgAAAApjbGlmZl90aW1lAAAAAAAGAAAAKlRva2VucyB1bmxvY2tlZCBhdCBjbGlmZiAoYWRkZWQgdG8gc3RhcnQpLgAAAAAAE2NsaWZmX3VubG9ja19hbW91bnQAAAAACwAAACZVbml4IHRpbWVzdGFtcCB3aGVuIHZlc3RpbmcgY29tcGxldGVzLgAAAAAACGVuZF90aW1lAAAABgAAADFVbmxvY2sgc3RlcCBpbnRlcnZhbCBpbiBzZWNvbmRzLiAwIGRlZmF1bHRzIHRvIDEuAAAAAAAAC2dyYW51bGFyaXR5AAAAAAYAAAAgQWRkcmVzcyByZWNlaXZpbmcgdmVzdGVkIHRva2Vucy4AAAAJcmVjaXBpZW50AAAAAAAAEwAAADhBZGRyZXNzIGZ1bmRpbmcgdGhlIHN0cmVhbSAoY2FuIGNhbmNlbCBpZiBgY2FuY2VsYWJsZWApLgAAAAZzZW5kZXIAAAAAABMAAAAjVW5peCB0aW1lc3RhbXAgd2hlbiB2ZXN0aW5nIGJlZ2lucy4AAAAACnN0YXJ0X3RpbWUAAAAAAAYAAAAlVG9rZW5zIHVubG9ja2VkIGltbWVkaWF0ZWx5IGF0IHN0YXJ0LgAAAAAAABNzdGFydF91bmxvY2tfYW1vdW50AAAAAAsAAAAfU29yb2JhbiB0b2tlbiBjb250cmFjdCBhZGRyZXNzLgAAAAAFdG9rZW4AAAAAAAATAAAAKVRvdGFsIHRva2VucyB0byB2ZXN0IChpbiB0b2tlbiBkZWNpbWFscykuAAAAAAAADHRvdGFsX2Ftb3VudAAAAAs=", - "AAAAAwAAAMZDYW5vbmljYWwgbGlmZWN5Y2xlIGV4cG9zZWQgYnkgdGhlIFJvdXRlciB0byBldmVyeSBwcm9kdWN0IHN1cmZhY2UuCgpDb3JlIGVuZ2luZXMgcmV0YWluIHRoZWlyIG1vcmUgZGV0YWlsZWQgYWNjb3VudGluZyBzdGF0ZXM7IHRoZSBSb3V0ZXIgbWFwcwp0aG9zZSBzdGF0ZXMgaW50byB0aGlzIHN0YWJsZSBjcm9zcy1lbmdpbmUgdm9jYWJ1bGFyeS4AAAAAAAAAAAAVQ2Fub25pY2FsU3RyZWFtU3RhdHVzAAAAAAAABgAAAAAAAAAHUGVuZGluZwAAAAAAAAAAAAAAAAZBY3RpdmUAAAAAAAEAAAAAAAAABlBhdXNlZAAAAAAAAgAAAAAAAAAIQ2FuY2VsZWQAAAADAAAAAAAAAAlDb21wbGV0ZWQAAAAAAAAEAAAAAAAAAAZGYWlsZWQAAAAAAAU=", "AAAABAAAACtFcnJvcnMgc3BlY2lmaWMgdG8gdGhlIFN0cmVhbSBORlQgY29udHJhY3QuAAAAAAAAAAAITmZ0RXJyb3IAAAAFAAAAAAAAABJBbHJlYWR5SW5pdGlhbGl6ZWQAAAAAAMkAAAAAAAAADU5vdEF1dGhvcml6ZWQAAAAAAADKAAAAAAAAAA1Ub2tlbk5vdEZvdW5kAAAAAAAAywAAAAAAAAAPTm90VHJhbnNmZXJhYmxlAAAAAMwAAAAhVG9rZW4gSUQgaGFzIGFscmVhZHkgYmVlbiBtaW50ZWQuAAAAAAAADUFscmVhZHlNaW50ZWQAAAAAAADN", - "AAAABAAAAC5FcnJvcnMgZW1pdHRlZCBieSB0aGUgRmxvdyBzdHJlYW1pbmcgY29udHJhY3QuAAAAAAAAAAAACUZsb3dFcnJvcgAAAAAAABYAAAAZU3RyZWFtIElEIGRvZXMgbm90IGV4aXN0LgAAAAAAAA5TdHJlYW1Ob3RGb3VuZAAAAAAAAQAAACxDYWxsZXIgaXMgbm90IGF1dGhvcml6ZWQgZm9yIHRoaXMgb3BlcmF0aW9uLgAAAAxVbmF1dGhvcml6ZWQAAAACAAAANlN0cmVhbSBpcyBwYXVzZWQ7IG9wZXJhdGlvbiByZXF1aXJlcyBhY3RpdmUgc3RyZWFtaW5nLgAAAAAADFN0cmVhbVBhdXNlZAAAAAMAAAAvU3RyZWFtIGlzIHZvaWRlZDsgbm8gZnVydGhlciBtdXRhdGlvbnMgYWxsb3dlZC4AAAAADFN0cmVhbVZvaWRlZAAAAAQAAAA3U3RyZWFtIGlzIG5vdCBwYXVzZWQ7IHJlc3RhcnQgcmVxdWlyZXMgYSBwYXVzZWQgc3RyZWFtLgAAAAAPU3RyZWFtTm90UGF1c2VkAAAAAAUAAAA5U3RyZWFtIGhhcyBub3Qgc3RhcnRlZCB5ZXQgKHNuYXBzaG90X3RpbWUgaW4gdGhlIGZ1dHVyZSkuAAAAAAAADVN0cmVhbVBlbmRpbmcAAAAAAAAGAAAAIE5ldyByYXRlIHBlciBzZWNvbmQgbXVzdCBiZSA+IDAuAAAAEVJhdGVQZXJTZWNvbmRaZXJvAAAAAAAABwAAACdOZXcgcmF0ZSBtdXN0IGRpZmZlciBmcm9tIGN1cnJlbnQgcmF0ZS4AAAAAEFJhdGVOb3REaWZmZXJlbnQAAAAIAAAAG0RlcG9zaXQgYW1vdW50IG11c3QgYmUgPiAwLgAAAAARRGVwb3NpdEFtb3VudFplcm8AAAAAAAAJAAAAHFdpdGhkcmF3IGFtb3VudCBtdXN0IGJlID4gMC4AAAASV2l0aGRyYXdBbW91bnRaZXJvAAAAAAAKAAAALVdpdGhkcmF3IGFtb3VudCBleGNlZWRzIHdpdGhkcmF3YWJsZSBiYWxhbmNlLgAAAAAAAAhPdmVyZHJhdwAAAAsAAAAaUmVmdW5kIGFtb3VudCBtdXN0IGJlID4gMC4AAAAAABBSZWZ1bmRBbW91bnRaZXJvAAAADAAAAClSZWZ1bmQgYW1vdW50IGV4Y2VlZHMgcmVmdW5kYWJsZSBiYWxhbmNlLgAAAAAAAA5SZWZ1bmRPdmVyZmxvdwAAAAAADQAAACVUb2tlbiBoYXMgPiAxOCBkZWNpbWFscywgdW5zdXBwb3J0ZWQuAAAAAAAAFEludmFsaWRUb2tlbkRlY2ltYWxzAAAADgAAADZTdHJlYW0gYmFsYW5jZSBpcyB6ZXJvIChlLmcuIHF1ZXJ5aW5nIGRlcGxldGlvbiB0aW1lKS4AAAAAAAtCYWxhbmNlWmVybwAAAAAPAAAAHUNvbnRyYWN0IGFscmVhZHkgaW5pdGlhbGl6ZWQuAAAAAAAAEkFscmVhZHlJbml0aWFsaXplZAAAAAAAEAAAAB1Db250cmFjdCBub3QgeWV0IGluaXRpYWxpemVkLgAAAAAAAA5Ob3RJbml0aWFsaXplZAAAAAAAEQAAADhDYW5ub3QgY3JlYXRlIGEgcGVuZGluZyBzdHJlYW0gd2l0aCByYXRlX3Blcl9zZWNvbmQgPSAwLgAAABdDcmVhdGVSYXRlUGVyU2Vjb25kWmVybwAAAAASAAAAOUludGVybmFsIG1hdGggZXJyb3Ig4oCUIHNob3VsZCBuZXZlciBvY2N1ciBpbiBwcm9kdWN0aW9uLgAAAAAAABJJbnZhbGlkQ2FsY3VsYXRpb24AAAAAABMAAAAxU2VuZGVyIGFuZCByZWNpcGllbnQgbXVzdCBiZSBkaWZmZXJlbnQgYWRkcmVzc2VzLgAAAAAAABVTZW5kZXJFcXVhbHNSZWNpcGllbnQAAAAAAAAUAAAAJVJhdGUgcGVyIHNlY29uZCBtdXN0IG5vdCBiZSBuZWdhdGl2ZS4AAAAAAAAMTmVnYXRpdmVSYXRlAAAAFQAAAD5UaGUgdG9rZW4gZGlkIG5vdCBkZWJpdCBhbmQgY3JlZGl0IHRoZSBleGFjdCByZXF1ZXN0ZWQgYW1vdW50LgAAAAAAFVRva2VuVHJhbnNmZXJNaXNtYXRjaAAAAAAAABY=", - "AAAABAAAAC5FcnJvcnMgZW1pdHRlZCBieSB0aGUgTG9ja3VwIHZlc3RpbmcgY29udHJhY3QuAAAAAAAAAAAAC0xvY2t1cEVycm9yAAAAAAwAAAAZU3RyZWFtIElEIGRvZXMgbm90IGV4aXN0LgAAAAAAAA5TdHJlYW1Ob3RGb3VuZAAAAAAAZQAAABlDYWxsZXIgaXMgbm90IGF1dGhvcml6ZWQuAAAAAAAADFVuYXV0aG9yaXplZAAAAGYAAAAcU3RyZWFtIGlzIGFscmVhZHkgY2FuY2VsbGVkLgAAABBBbHJlYWR5Q2FuY2VsbGVkAAAAZwAAABlTdHJlYW0gaXMgbm90IGNhbmNlbGFibGUuAAAAAAAADU5vdENhbmNlbGFibGUAAAAAAABoAAAAKVdpdGhkcmF3IGFtb3VudCBleGNlZWRzIHVubG9ja2VkIGJhbGFuY2UuAAAAAAAACE92ZXJkcmF3AAAAaQAAADxJbnZhbGlkIHRpbWUgcGFyYW1ldGVycyAoc3RhcnQgPj0gZW5kLCBjbGlmZiBvdXRzaWRlIHJhbmdlKS4AAAAQSW52YWxpZFRpbWVSYW5nZQAAAGoAAAAZVG90YWwgYW1vdW50IG11c3QgYmUgPiAwLgAAAAAAAApBbW91bnRaZXJvAAAAAABrAAAAHUNvbnRyYWN0IGFscmVhZHkgaW5pdGlhbGl6ZWQuAAAAAAAAEkFscmVhZHlJbml0aWFsaXplZAAAAAAAbAAAAB1Db250cmFjdCBub3QgeWV0IGluaXRpYWxpemVkLgAAAAAAAA5Ob3RJbml0aWFsaXplZAAAAAAAbQAAADFTZW5kZXIgYW5kIHJlY2lwaWVudCBtdXN0IGJlIGRpZmZlcmVudCBhZGRyZXNzZXMuAAAAAAAAFVNlbmRlckVxdWFsc1JlY2lwaWVudAAAAAAAAG4AAABLU3RhcnQgYW5kIGNsaWZmIHVubG9jayBhbW91bnRzIG11c3QgYmUgbm9ubmVnYXRpdmUgYW5kIGZpdCBpbiB0b3RhbCBhbW91bnQuAAAAABNJbnZhbGlkVW5sb2NrQW1vdW50AAAAAG8AAAA+VGhlIHRva2VuIGRpZCBub3QgZGViaXQgYW5kIGNyZWRpdCB0aGUgZXhhY3QgcmVxdWVzdGVkIGFtb3VudC4AAAAAABVUb2tlblRyYW5zZmVyTWlzbWF0Y2gAAAAAAABw", - "AAAABAAAACdFcnJvcnMgc3BlY2lmaWMgdG8gdGhlIFJvdXRlciBjb250cmFjdC4AAAAAAAAAAAtSb3V0ZXJFcnJvcgAAAAAFAAAAAAAAABJBbHJlYWR5SW5pdGlhbGl6ZWQAAAAAAS0AAAAAAAAADk5vdEluaXRpYWxpemVkAAAAAAEuAAAAAAAAAA1Ob3RBdXRob3JpemVkAAAAAAABLwAAAAAAAAARSW52YWxpZFN0cmVhbVR5cGUAAAAAAAEwAAAAMENvcmUgY29udHJhY3QgYWRkcmVzc2VzIHdlcmUgYWxyZWFkeSBjb25maWd1cmVkLgAAABFBbHJlYWR5Q29uZmlndXJlZAAAAAAAATE=", - "AAAAAgAAAKFTdG9yYWdlIGtleXMgZm9yIGNvbnRyYWN0IGRhdGEuCgpVc2luZyBhIHR5cGVkIGVudW0gcHJldmVudHMga2V5IGNvbGxpc2lvbnMgKFNLSUxMLm1kIMKnMykuCktleXMgYXJlIG5hbWVzcGFjZWQgYnkgdmFyaWFudCB0byBrZWVwIGRpZmZlcmVudCBkYXRhIHR5cGVzIHNlcGFyYXRlLgAAAAAAAAAAAAAHRGF0YUtleQAAAAAPAAAAAAAAACFBZG1pbiBhZGRyZXNzIChJbnN0YW5jZSBzdG9yYWdlKS4AAAAAAAAFQWRtaW4AAAAAAAAAAAAAKk5leHQgc3RyZWFtIElEIGNvdW50ZXIgKEluc3RhbmNlIHN0b3JhZ2UpLgAAAAAADE5leHRTdHJlYW1JZAAAAAEAAAA+QSBGbG93IHN0cmVhbSByZWNvcmQsIGtleWVkIGJ5IHN0cmVhbSBJRCAoUGVyc2lzdGVudCBzdG9yYWdlKS4AAAAAAApGbG93U3RyZWFtAAAAAAABAAAABgAAAAEAAABAQSBMb2NrdXAgc3RyZWFtIHJlY29yZCwga2V5ZWQgYnkgc3RyZWFtIElEIChQZXJzaXN0ZW50IHN0b3JhZ2UpLgAAAAxMb2NrdXBTdHJlYW0AAAABAAAABgAAAAEAAACTQWdncmVnYXRlIHRva2VuIGJhbGFuY2UgaGVsZCBieSB0aGUgY29udHJhY3QgZm9yIGEgZ2l2ZW4gdG9rZW4gYWRkcmVzcy4KVXNlZCBmb3Igc3VycGx1cyByZWNvdmVyeSAvIGFjY291bnRpbmcgcmVjb25jaWxpYXRpb24gKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAAABBBZ2dyZWdhdGVCYWxhbmNlAAAAAQAAABMAAAABAAAAOE5GVCB0b2tlbiBvd25lciwga2V5ZWQgYnkgdG9rZW4gSUQgKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAAClRva2VuT3duZXIAAAAAAAEAAAALAAAAAQAAAExORlQgc3RyZWFtIGRhdGEgbWFwcGluZyB0b2tlbiBJRCB0byBzdHJlYW0gdHlwZSBhbmQgSUQgKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAAD1Rva2VuU3RyZWFtRGF0YQAAAAABAAAACwAAAAEAAABFSW1tdXRhYmxlIHBlci1zdHJlYW0gTkZUIHRyYW5zZmVyYWJpbGl0eSBwb2xpY3kgKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAAAAAAEVRva2VuVHJhbnNmZXJhYmxlAAAAAAAAAQAAAAsAAAABAAAAOE51bWJlciBvZiBORlRzIG93bmVkIGJ5IGFuIGFkZHJlc3MgKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAACk5mdEJhbGFuY2UAAAAAAAEAAAATAAAAAQAAADtUb2tlbiBtZXRhZGF0YSwgZS5nLiwgbmFtZSwgc3ltYm9sLCBVUkkgKEluc3RhbmNlIHN0b3JhZ2UpLgAAAAANVG9rZW5NZXRhZGF0YQAAAAAAAAEAAAARAAAAAAAAACxSb3V0ZXIgY29uZmlndXJhdGlvbjogRmxvdyBjb250cmFjdCBhZGRyZXNzLgAAAAxGbG93Q29udHJhY3QAAAAAAAAALlJvdXRlciBjb25maWd1cmF0aW9uOiBMb2NrdXAgY29udHJhY3QgYWRkcmVzcy4AAAAAAA5Mb2NrdXBDb250cmFjdAAAAAAAAAAAACtSb3V0ZXIgY29uZmlndXJhdGlvbjogTkZUIGNvbnRyYWN0IGFkZHJlc3MuAAAAAAtOZnRDb250cmFjdAAAAAAAAAAAO0NvcmUtZW5naW5lIGNvbmZpZ3VyYXRpb246IHRydXN0ZWQgUm91dGVyIGNvbnRyYWN0IGFkZHJlc3MuAAAAAAZSb3V0ZXIAAAAAAAAAAAA9UGF5bWFzdGVyIGNvbmZpZ3VyYXRpb246IGxpc3Qgb2YgYWxsb3dlZCBmZWUgdG9rZW4gYWRkcmVzc2VzLgAAAAAAABBBbGxvd2VkRmVlVG9rZW5z" ]), + "AAAABAAAAC5FcnJvcnMgZW1pdHRlZCBieSB0aGUgRmxvdyBzdHJlYW1pbmcgY29udHJhY3QuAAAAAAAAAAAACUZsb3dFcnJvcgAAAAAAABwAAAAZU3RyZWFtIElEIGRvZXMgbm90IGV4aXN0LgAAAAAAAA5TdHJlYW1Ob3RGb3VuZAAAAAAAAQAAACxDYWxsZXIgaXMgbm90IGF1dGhvcml6ZWQgZm9yIHRoaXMgb3BlcmF0aW9uLgAAAAxVbmF1dGhvcml6ZWQAAAACAAAANlN0cmVhbSBpcyBwYXVzZWQ7IG9wZXJhdGlvbiByZXF1aXJlcyBhY3RpdmUgc3RyZWFtaW5nLgAAAAAADFN0cmVhbVBhdXNlZAAAAAMAAAAvU3RyZWFtIGlzIHZvaWRlZDsgbm8gZnVydGhlciBtdXRhdGlvbnMgYWxsb3dlZC4AAAAADFN0cmVhbVZvaWRlZAAAAAQAAAA3U3RyZWFtIGlzIG5vdCBwYXVzZWQ7IHJlc3RhcnQgcmVxdWlyZXMgYSBwYXVzZWQgc3RyZWFtLgAAAAAPU3RyZWFtTm90UGF1c2VkAAAAAAUAAAA5U3RyZWFtIGhhcyBub3Qgc3RhcnRlZCB5ZXQgKHNuYXBzaG90X3RpbWUgaW4gdGhlIGZ1dHVyZSkuAAAAAAAADVN0cmVhbVBlbmRpbmcAAAAAAAAGAAAAIE5ldyByYXRlIHBlciBzZWNvbmQgbXVzdCBiZSA+IDAuAAAAEVJhdGVQZXJTZWNvbmRaZXJvAAAAAAAABwAAACdOZXcgcmF0ZSBtdXN0IGRpZmZlciBmcm9tIGN1cnJlbnQgcmF0ZS4AAAAAEFJhdGVOb3REaWZmZXJlbnQAAAAIAAAAG0RlcG9zaXQgYW1vdW50IG11c3QgYmUgPiAwLgAAAAARRGVwb3NpdEFtb3VudFplcm8AAAAAAAAJAAAAHFdpdGhkcmF3IGFtb3VudCBtdXN0IGJlID4gMC4AAAASV2l0aGRyYXdBbW91bnRaZXJvAAAAAAAKAAAALVdpdGhkcmF3IGFtb3VudCBleGNlZWRzIHdpdGhkcmF3YWJsZSBiYWxhbmNlLgAAAAAAAAhPdmVyZHJhdwAAAAsAAAAaUmVmdW5kIGFtb3VudCBtdXN0IGJlID4gMC4AAAAAABBSZWZ1bmRBbW91bnRaZXJvAAAADAAAAClSZWZ1bmQgYW1vdW50IGV4Y2VlZHMgcmVmdW5kYWJsZSBiYWxhbmNlLgAAAAAAAA5SZWZ1bmRPdmVyZmxvdwAAAAAADQAAACVUb2tlbiBoYXMgPiAxOCBkZWNpbWFscywgdW5zdXBwb3J0ZWQuAAAAAAAAFEludmFsaWRUb2tlbkRlY2ltYWxzAAAADgAAADZTdHJlYW0gYmFsYW5jZSBpcyB6ZXJvIChlLmcuIHF1ZXJ5aW5nIGRlcGxldGlvbiB0aW1lKS4AAAAAAAtCYWxhbmNlWmVybwAAAAAPAAAAHUNvbnRyYWN0IGFscmVhZHkgaW5pdGlhbGl6ZWQuAAAAAAAAEkFscmVhZHlJbml0aWFsaXplZAAAAAAAEAAAAB1Db250cmFjdCBub3QgeWV0IGluaXRpYWxpemVkLgAAAAAAAA5Ob3RJbml0aWFsaXplZAAAAAAAEQAAADhDYW5ub3QgY3JlYXRlIGEgcGVuZGluZyBzdHJlYW0gd2l0aCByYXRlX3Blcl9zZWNvbmQgPSAwLgAAABdDcmVhdGVSYXRlUGVyU2Vjb25kWmVybwAAAAASAAAAOUludGVybmFsIG1hdGggZXJyb3Ig4oCUIHNob3VsZCBuZXZlciBvY2N1ciBpbiBwcm9kdWN0aW9uLgAAAAAAABJJbnZhbGlkQ2FsY3VsYXRpb24AAAAAABMAAAAxU2VuZGVyIGFuZCByZWNpcGllbnQgbXVzdCBiZSBkaWZmZXJlbnQgYWRkcmVzc2VzLgAAAAAAABVTZW5kZXJFcXVhbHNSZWNpcGllbnQAAAAAAAAUAAAAJVJhdGUgcGVyIHNlY29uZCBtdXN0IG5vdCBiZSBuZWdhdGl2ZS4AAAAAAAAMTmVnYXRpdmVSYXRlAAAAFQAAAEVUb2tlbiB0cmFuc2ZlciByZXN1bHRlZCBpbiBhIGRpZmZlcmVudCBiYWxhbmNlIGNoYW5nZSB0aGFuIHJlcXVlc3RlZC4AAAAAAAAVVG9rZW5UcmFuc2Zlck1pc21hdGNoAAAAAAAAFgAAAD9DYWxsZXItc3VwcGxpZWQgdG9rZW4gZGVjaW1hbHMgZG8gbm90IG1hdGNoIHRoZSB0b2tlbiBjb250cmFjdC4AAAAAFVRva2VuRGVjaW1hbHNNaXNtYXRjaAAAAAAAABcAAAA5Q2hlY2tlZCBhcml0aG1ldGljIG9wZXJhdGlvbiBmYWlsZWQgKG92ZXJmbG93L3VuZGVyZmxvdykuAAAAAAAAD0FyaXRobWV0aWNFcnJvcgAAAAAYAAAAH0FkbWluIHRyYW5zZmVyIGFscmVhZHkgcGVuZGluZy4AAAAAFEFkbWluVHJhbnNmZXJQZW5kaW5nAAAAGQAAACRObyBhZG1pbiB0cmFuc2ZlciBwZW5kaW5nIHRvIGFjY2VwdC4AAAAWTm9BZG1pblRyYW5zZmVyUGVuZGluZwAAAAAAGgAAADFVcGdyYWRlIGlzIHRpbWVsb2NrZWQgYW5kIGNhbm5vdCBiZSBleGVjdXRlZCB5ZXQuAAAAAAAAEVVwZ3JhZGVUaW1lbG9ja2VkAAAAAAAAGwAAAB1ObyB1cGdyYWRlIGhhcyBiZWVuIHByb3Bvc2VkLgAAAAAAABFOb1VwZ3JhZGVQcm9wb3NlZAAAAAAAABw=", + "AAAABAAAAC5FcnJvcnMgZW1pdHRlZCBieSB0aGUgTG9ja3VwIHZlc3RpbmcgY29udHJhY3QuAAAAAAAAAAAAC0xvY2t1cEVycm9yAAAAABQAAAAZU3RyZWFtIElEIGRvZXMgbm90IGV4aXN0LgAAAAAAAA5TdHJlYW1Ob3RGb3VuZAAAAAAAZQAAABlDYWxsZXIgaXMgbm90IGF1dGhvcml6ZWQuAAAAAAAADFVuYXV0aG9yaXplZAAAAGYAAAAcU3RyZWFtIGlzIGFscmVhZHkgY2FuY2VsbGVkLgAAABBBbHJlYWR5Q2FuY2VsbGVkAAAAZwAAABlTdHJlYW0gaXMgbm90IGNhbmNlbGFibGUuAAAAAAAADU5vdENhbmNlbGFibGUAAAAAAABoAAAAKVdpdGhkcmF3IGFtb3VudCBleGNlZWRzIHVubG9ja2VkIGJhbGFuY2UuAAAAAAAACE92ZXJkcmF3AAAAaQAAADxJbnZhbGlkIHRpbWUgcGFyYW1ldGVycyAoc3RhcnQgPj0gZW5kLCBjbGlmZiBvdXRzaWRlIHJhbmdlKS4AAAAQSW52YWxpZFRpbWVSYW5nZQAAAGoAAAAZVG90YWwgYW1vdW50IG11c3QgYmUgPiAwLgAAAAAAAApBbW91bnRaZXJvAAAAAABrAAAAHUNvbnRyYWN0IGFscmVhZHkgaW5pdGlhbGl6ZWQuAAAAAAAAEkFscmVhZHlJbml0aWFsaXplZAAAAAAAbAAAAB1Db250cmFjdCBub3QgeWV0IGluaXRpYWxpemVkLgAAAAAAAA5Ob3RJbml0aWFsaXplZAAAAAAAbQAAADFTZW5kZXIgYW5kIHJlY2lwaWVudCBtdXN0IGJlIGRpZmZlcmVudCBhZGRyZXNzZXMuAAAAAAAAFVNlbmRlckVxdWFsc1JlY2lwaWVudAAAAAAAAG4AAAA3c3RhcnRfdW5sb2NrX2Ftb3VudCBvciBjbGlmZl91bmxvY2tfYW1vdW50IGlzIG5lZ2F0aXZlLgAAAAAUTmVnYXRpdmVVbmxvY2tBbW91bnQAAABvAAAALkNoZWNrZWQgYWRkaXRpb24gb2YgdW5sb2NrIGFtb3VudHMgb3ZlcmZsb3dlZC4AAAAAABFVbmxvY2tTdW1PdmVyZmxvdwAAAAAAAHAAAAA3VW5sb2NrIHN1bSBpcyBub3QgaW4gdGhlIHZhbGlkIHJhbmdlIFswLCB0b3RhbF9hbW91bnRdLgAAAAAQSW52YWxpZFVubG9ja1N1bQAAAHEAAAAwRGVmZW5zaXZlIGNhbmNlbGxhdGlvbiBhbW91bnQgdmFsaWRhdGlvbiBmYWlsZWQuAAAAGUludmFsaWRDYW5jZWxsYXRpb25BbW91bnQAAAAAAAByAAAARVRva2VuIHRyYW5zZmVyIHJlc3VsdGVkIGluIGEgZGlmZmVyZW50IGJhbGFuY2UgY2hhbmdlIHRoYW4gcmVxdWVzdGVkLgAAAAAAABVUb2tlblRyYW5zZmVyTWlzbWF0Y2gAAAAAAABzAAAAOUNoZWNrZWQgYXJpdGhtZXRpYyBvcGVyYXRpb24gZmFpbGVkIChvdmVyZmxvdy91bmRlcmZsb3cpLgAAAAAAAA9Bcml0aG1ldGljRXJyb3IAAAAAdAAAAB9BZG1pbiB0cmFuc2ZlciBhbHJlYWR5IHBlbmRpbmcuAAAAABRBZG1pblRyYW5zZmVyUGVuZGluZwAAAHUAAAAkTm8gYWRtaW4gdHJhbnNmZXIgcGVuZGluZyB0byBhY2NlcHQuAAAAFk5vQWRtaW5UcmFuc2ZlclBlbmRpbmcAAAAAAHYAAAAxVXBncmFkZSBpcyB0aW1lbG9ja2VkIGFuZCBjYW5ub3QgYmUgZXhlY3V0ZWQgeWV0LgAAAAAAABFVcGdyYWRlVGltZWxvY2tlZAAAAAAAAHcAAAAdTm8gdXBncmFkZSBoYXMgYmVlbiBwcm9wb3NlZC4AAAAAAAARTm9VcGdyYWRlUHJvcG9zZWQAAAAAAAB4", + "AAAABAAAACdFcnJvcnMgc3BlY2lmaWMgdG8gdGhlIFJvdXRlciBjb250cmFjdC4AAAAAAAAAAAtSb3V0ZXJFcnJvcgAAAAAFAAAAAAAAABJBbHJlYWR5SW5pdGlhbGl6ZWQAAAAAAS0AAAAAAAAADk5vdEluaXRpYWxpemVkAAAAAAEuAAAAAAAAAA1Ob3RBdXRob3JpemVkAAAAAAABLwAAAAAAAAARSW52YWxpZFN0cmVhbVR5cGUAAAAAAAEwAAAANEEgcmVxdWlyZWQgY29udHJhY3QgYWRkcmVzcyBpcyBpbnZhbGlkICh6ZXJvL3Vuc2V0KS4AAAAWSW52YWxpZENvbnRyYWN0QWRkcmVzcwAAAAABMQ==", + "AAAAAgAAAKFTdG9yYWdlIGtleXMgZm9yIGNvbnRyYWN0IGRhdGEuCgpVc2luZyBhIHR5cGVkIGVudW0gcHJldmVudHMga2V5IGNvbGxpc2lvbnMgKFNLSUxMLm1kIMKnMykuCktleXMgYXJlIG5hbWVzcGFjZWQgYnkgdmFyaWFudCB0byBrZWVwIGRpZmZlcmVudCBkYXRhIHR5cGVzIHNlcGFyYXRlLgAAAAAAAAAAAAAHRGF0YUtleQAAAAAQAAAAAAAAACFBZG1pbiBhZGRyZXNzIChJbnN0YW5jZSBzdG9yYWdlKS4AAAAAAAAFQWRtaW4AAAAAAAAAAAAAKk5leHQgc3RyZWFtIElEIGNvdW50ZXIgKEluc3RhbmNlIHN0b3JhZ2UpLgAAAAAADE5leHRTdHJlYW1JZAAAAAEAAAA+QSBGbG93IHN0cmVhbSByZWNvcmQsIGtleWVkIGJ5IHN0cmVhbSBJRCAoUGVyc2lzdGVudCBzdG9yYWdlKS4AAAAAAApGbG93U3RyZWFtAAAAAAABAAAABgAAAAEAAABAQSBMb2NrdXAgc3RyZWFtIHJlY29yZCwga2V5ZWQgYnkgc3RyZWFtIElEIChQZXJzaXN0ZW50IHN0b3JhZ2UpLgAAAAxMb2NrdXBTdHJlYW0AAAABAAAABgAAAAEAAACTQWdncmVnYXRlIHRva2VuIGJhbGFuY2UgaGVsZCBieSB0aGUgY29udHJhY3QgZm9yIGEgZ2l2ZW4gdG9rZW4gYWRkcmVzcy4KVXNlZCBmb3Igc3VycGx1cyByZWNvdmVyeSAvIGFjY291bnRpbmcgcmVjb25jaWxpYXRpb24gKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAAABBBZ2dyZWdhdGVCYWxhbmNlAAAAAQAAABMAAAABAAAAOE5GVCB0b2tlbiBvd25lciwga2V5ZWQgYnkgdG9rZW4gSUQgKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAAClRva2VuT3duZXIAAAAAAAEAAAALAAAAAQAAAExORlQgc3RyZWFtIGRhdGEgbWFwcGluZyB0b2tlbiBJRCB0byBzdHJlYW0gdHlwZSBhbmQgSUQgKFBlcnNpc3RlbnQgc3RvcmFnZSkuAAAAD1Rva2VuU3RyZWFtRGF0YQAAAAABAAAACwAAAAEAAAA4TnVtYmVyIG9mIE5GVHMgb3duZWQgYnkgYW4gYWRkcmVzcyAoUGVyc2lzdGVudCBzdG9yYWdlKS4AAAAKTmZ0QmFsYW5jZQAAAAAAAQAAABMAAAABAAAAO1Rva2VuIG1ldGFkYXRhLCBlLmcuLCBuYW1lLCBzeW1ib2wsIFVSSSAoSW5zdGFuY2Ugc3RvcmFnZSkuAAAAAA1Ub2tlbk1ldGFkYXRhAAAAAAAAAQAAABEAAAAAAAAALFJvdXRlciBjb25maWd1cmF0aW9uOiBGbG93IGNvbnRyYWN0IGFkZHJlc3MuAAAADEZsb3dDb250cmFjdAAAAAAAAAAuUm91dGVyIGNvbmZpZ3VyYXRpb246IExvY2t1cCBjb250cmFjdCBhZGRyZXNzLgAAAAAADkxvY2t1cENvbnRyYWN0AAAAAAAAAAAAK1JvdXRlciBjb25maWd1cmF0aW9uOiBORlQgY29udHJhY3QgYWRkcmVzcy4AAAAAC05mdENvbnRyYWN0AAAAAAAAAAA9UGF5bWFzdGVyIGNvbmZpZ3VyYXRpb246IGxpc3Qgb2YgYWxsb3dlZCBmZWUgdG9rZW4gYWRkcmVzc2VzLgAAAAAAABBBbGxvd2VkRmVlVG9rZW5zAAAAAAAAAEVQZW5kaW5nIGFkbWluIGFkZHJlc3MgZm9yIHR3by1zdGVwIGFkbWluIHRyYW5zZmVyIChJbnN0YW5jZSBzdG9yYWdlKS4AAAAAAAAMUGVuZGluZ0FkbWluAAAAAQAAAC5Qcm9wb3NlZCB1cGdyYWRlIFdBU00gaGFzaCAoSW5zdGFuY2Ugc3RvcmFnZSkuAAAAAAAPUHJvcG9zZWRVcGdyYWRlAAAAAAEAAAPuAAAAIAAAAAAAAABSTGVkZ2VyIHNlcXVlbmNlIGF0IHdoaWNoIGEgcHJvcG9zZWQgdXBncmFkZSBiZWNvbWVzIGV4ZWN1dGFibGUgKEluc3RhbmNlIHN0b3JhZ2UpLgAAAAAAE1VwZ3JhZGVVbmxvY2tMZWRnZXIA" ]), options ) } public readonly fromJSON = { - mint: this.txFromJSON, + burn: this.txFromJSON, + mint: this.txFromJSON, name: this.txFromJSON, symbol: this.txFromJSON, balance: this.txFromJSON, upgrade: this.txFromJSON, owner_of: this.txFromJSON, transfer: this.txFromJSON, - get_stream_data: this.txFromJSON, - is_transferable: this.txFromJSON + initialize: this.txFromJSON, + get_stream_data: this.txFromJSON } } diff --git a/src/index.ts b/src/index.ts index 2caecc6..0d95304 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ /** Fundable Protocol multichain SDK. Stellar is the first implemented adapter. */ -export const VERSION = "0.2.0"; +export const VERSION = "0.2.3"; export * from "./client.js"; export * from "./core/index.js"; diff --git a/src/stellar/admin-client.ts b/src/stellar/admin-client.ts new file mode 100644 index 0000000..2aece7c --- /dev/null +++ b/src/stellar/admin-client.ts @@ -0,0 +1,341 @@ +import { Buffer } from "buffer"; +import { FUNDABLE_ERROR_CODES, FundableError } from "../core/index.js"; +import { Client as GeneratedFlowClient } from "../generated/flow/src/index.js"; +import { Client as GeneratedLockupClient } from "../generated/lockup/src/index.js"; +import { Client as GeneratedRouterClient } from "../generated/router/src/index.js"; +import { toFundableError } from "./error-parser.js"; +import type { StellarTransaction } from "./flow-client.js"; +import type { StellarFundableClientConfig, StellarMethodOptions } from "./types.js"; +import { + assertContractId, + assertStellarAddress, + toStreamId, +} from "./validation.js"; + +export function toWasmHashBuffer(hash: string | Buffer | Uint8Array): Buffer { + if (Buffer.isBuffer(hash)) { + if (hash.length !== 32) { + throw new FundableError({ + code: FUNDABLE_ERROR_CODES.INVALID_ARGUMENT, + message: "WASM hash must be exactly 32 bytes.", + chain: "stellar", + }); + } + return hash; + } + + if (hash instanceof Uint8Array) { + if (hash.length !== 32) { + throw new FundableError({ + code: FUNDABLE_ERROR_CODES.INVALID_ARGUMENT, + message: "WASM hash must be exactly 32 bytes.", + chain: "stellar", + }); + } + return Buffer.from(hash); + } + + if (typeof hash === "string") { + const clean = hash.trim().replace(/^0x/i, ""); + if (!/^[0-9a-fA-F]{64}$/.test(clean)) { + throw new FundableError({ + code: FUNDABLE_ERROR_CODES.INVALID_ARGUMENT, + message: "WASM hash must be a 64-character hex string (32 bytes).", + chain: "stellar", + }); + } + return Buffer.from(clean, "hex"); + } + + throw new FundableError({ + code: FUNDABLE_ERROR_CODES.INVALID_ARGUMENT, + message: "WASM hash must be a hex string or 32-byte Buffer.", + chain: "stellar", + }); +} + +export class StellarFlowAdminClient { + private readonly client: GeneratedFlowClient; + + constructor(config: StellarFundableClientConfig) { + assertContractId(config.contracts.flow, "Flow contract"); + this.client = new GeneratedFlowClient({ + contractId: config.contracts.flow, + networkPassphrase: config.networkPassphrase, + rpcUrl: config.rpcUrl, + publicKey: config.publicKey, + allowHttp: config.allowHttp, + headers: config.headers, + signTransaction: config.signTransaction, + signAuthEntry: config.signAuthEntry, + }); + } + + async proposeUpgrade( + newWasmHash: string | Buffer | Uint8Array, + options?: StellarMethodOptions, + ): Promise> { + try { + const hash = toWasmHashBuffer(newWasmHash); + return await this.client.propose_upgrade({ new_wasm_hash: hash }, options); + } catch (error) { + throw toFundableError(error, "Failed to propose Flow contract upgrade."); + } + } + + async executeUpgrade( + newWasmHash: string | Buffer | Uint8Array, + options?: StellarMethodOptions, + ): Promise> { + try { + const hash = toWasmHashBuffer(newWasmHash); + return await this.client.execute_upgrade({ new_wasm_hash: hash }, options); + } catch (error) { + throw toFundableError(error, "Failed to execute Flow contract upgrade."); + } + } + + async upgrade( + newWasmHash: string | Buffer | Uint8Array, + options?: StellarMethodOptions, + ): Promise> { + try { + const hash = toWasmHashBuffer(newWasmHash); + return await this.client.upgrade({ new_wasm_hash: hash }, options); + } catch (error) { + throw toFundableError(error, "Failed to upgrade Flow contract."); + } + } + + async proposeAdmin( + newAdmin: string, + options?: StellarMethodOptions, + ): Promise> { + assertStellarAddress(newAdmin, "New admin address"); + try { + return await this.client.propose_admin({ new_admin: newAdmin }, options); + } catch (error) { + throw toFundableError(error, "Failed to propose Flow admin."); + } + } + + async acceptAdmin(options?: StellarMethodOptions): Promise> { + try { + return await this.client.accept_admin(options); + } catch (error) { + throw toFundableError(error, "Failed to accept Flow admin transfer."); + } + } + + async extendStreamTtl( + streamId: string | bigint, + options?: StellarMethodOptions, + ): Promise> { + try { + return await this.client.extend_stream_ttl( + { stream_id: toStreamId(streamId) }, + options, + ); + } catch (error) { + throw toFundableError(error, "Failed to extend Flow stream TTL."); + } + } +} + +export class StellarLockupAdminClient { + private readonly client: GeneratedLockupClient; + + constructor(config: StellarFundableClientConfig & { contracts: { lockup: string } }) { + assertContractId(config.contracts.lockup, "Lockup contract"); + this.client = new GeneratedLockupClient({ + contractId: config.contracts.lockup, + networkPassphrase: config.networkPassphrase, + rpcUrl: config.rpcUrl, + publicKey: config.publicKey, + allowHttp: config.allowHttp, + headers: config.headers, + signTransaction: config.signTransaction, + signAuthEntry: config.signAuthEntry, + }); + } + + async proposeUpgrade( + newWasmHash: string | Buffer | Uint8Array, + options?: StellarMethodOptions, + ): Promise> { + try { + const hash = toWasmHashBuffer(newWasmHash); + return await this.client.propose_upgrade({ new_wasm_hash: hash }, options); + } catch (error) { + throw toFundableError(error, "Failed to propose Lockup contract upgrade."); + } + } + + async executeUpgrade( + newWasmHash: string | Buffer | Uint8Array, + options?: StellarMethodOptions, + ): Promise> { + try { + const hash = toWasmHashBuffer(newWasmHash); + return await this.client.execute_upgrade({ new_wasm_hash: hash }, options); + } catch (error) { + throw toFundableError(error, "Failed to execute Lockup contract upgrade."); + } + } + + async upgrade( + newWasmHash: string | Buffer | Uint8Array, + options?: StellarMethodOptions, + ): Promise> { + try { + const hash = toWasmHashBuffer(newWasmHash); + return await this.client.upgrade({ new_wasm_hash: hash }, options); + } catch (error) { + throw toFundableError(error, "Failed to upgrade Lockup contract."); + } + } + + async proposeAdmin( + newAdmin: string, + options?: StellarMethodOptions, + ): Promise> { + assertStellarAddress(newAdmin, "New admin address"); + try { + return await this.client.propose_admin({ new_admin: newAdmin }, options); + } catch (error) { + throw toFundableError(error, "Failed to propose Lockup admin."); + } + } + + async acceptAdmin(options?: StellarMethodOptions): Promise> { + try { + return await this.client.accept_admin(options); + } catch (error) { + throw toFundableError(error, "Failed to accept Lockup admin transfer."); + } + } + + async extendStreamTtl( + streamId: string | bigint, + options?: StellarMethodOptions, + ): Promise> { + try { + return await this.client.extend_stream_ttl( + { stream_id: toStreamId(streamId) }, + options, + ); + } catch (error) { + throw toFundableError(error, "Failed to extend Lockup stream TTL."); + } + } +} + +export class StellarRouterAdminClient { + private readonly client: GeneratedRouterClient; + + constructor(config: StellarFundableClientConfig & { contracts: { router: string } }) { + assertContractId(config.contracts.router, "Router contract"); + this.client = new GeneratedRouterClient({ + contractId: config.contracts.router, + networkPassphrase: config.networkPassphrase, + rpcUrl: config.rpcUrl, + publicKey: config.publicKey, + allowHttp: config.allowHttp, + headers: config.headers, + signTransaction: config.signTransaction, + signAuthEntry: config.signAuthEntry, + }); + } + + async proposeUpgrade( + newWasmHash: string | Buffer | Uint8Array, + options?: StellarMethodOptions, + ): Promise> { + try { + const hash = toWasmHashBuffer(newWasmHash); + return await this.client.propose_upgrade({ new_wasm_hash: hash }, options); + } catch (error) { + throw toFundableError(error, "Failed to propose Router contract upgrade."); + } + } + + async executeUpgrade( + newWasmHash: string | Buffer | Uint8Array, + options?: StellarMethodOptions, + ): Promise> { + try { + const hash = toWasmHashBuffer(newWasmHash); + return await this.client.execute_upgrade({ new_wasm_hash: hash }, options); + } catch (error) { + throw toFundableError(error, "Failed to execute Router contract upgrade."); + } + } + + async upgrade( + newWasmHash: string | Buffer | Uint8Array, + options?: StellarMethodOptions, + ): Promise> { + try { + const hash = toWasmHashBuffer(newWasmHash); + return await this.client.upgrade({ new_wasm_hash: hash }, options); + } catch (error) { + throw toFundableError(error, "Failed to upgrade Router contract."); + } + } + + async upgradeNft( + newWasmHash: string | Buffer | Uint8Array, + options?: StellarMethodOptions, + ): Promise> { + try { + const hash = toWasmHashBuffer(newWasmHash); + return await this.client.upgrade_nft({ new_wasm_hash: hash }, options); + } catch (error) { + throw toFundableError(error, "Failed to upgrade NFT contract via Router."); + } + } + + async proposeAdmin( + newAdmin: string, + options?: StellarMethodOptions, + ): Promise> { + assertStellarAddress(newAdmin, "New admin address"); + try { + return await this.client.propose_admin({ new_admin: newAdmin }, options); + } catch (error) { + throw toFundableError(error, "Failed to propose Router admin."); + } + } + + async acceptAdmin(options?: StellarMethodOptions): Promise> { + try { + return await this.client.accept_admin(options); + } catch (error) { + throw toFundableError(error, "Failed to accept Router admin transfer."); + } + } +} + +export class StellarAdminClient { + readonly flows: StellarFlowAdminClient; + readonly lockups?: StellarLockupAdminClient; + readonly router?: StellarRouterAdminClient; + + constructor(config: StellarFundableClientConfig) { + this.flows = new StellarFlowAdminClient(config); + + if (config.contracts.lockup) { + this.lockups = new StellarLockupAdminClient({ + ...config, + contracts: { ...config.contracts, lockup: config.contracts.lockup }, + }); + } + + if (config.contracts.router) { + this.router = new StellarRouterAdminClient({ + ...config, + contracts: { ...config.contracts, router: config.contracts.router }, + }); + } + } +} diff --git a/src/stellar/client.ts b/src/stellar/client.ts index e55cde2..e7453a0 100644 --- a/src/stellar/client.ts +++ b/src/stellar/client.ts @@ -1,4 +1,5 @@ import { FUNDABLE_ERROR_CODES, FundableError } from "../core/index.js"; +import { StellarAdminClient } from "./admin-client.js"; import { StellarFlowClient } from "./flow-client.js"; import { StellarLockupClient } from "./lockup-client.js"; import { StellarPaymasterClient } from "./paymaster-client.js"; @@ -18,6 +19,7 @@ export class StellarFundableClient { /** @deprecated Production integrations must use `sponsorship`. */ readonly paymaster?: StellarPaymasterClient; readonly sponsorship?: StellarSponsorshipClient; + readonly admin: StellarAdminClient; constructor(readonly config: StellarFundableClientConfig) { if (!config.rpcUrl || !config.networkPassphrase) { @@ -62,6 +64,7 @@ export class StellarFundableClient { if (config.sponsorship) { this.sponsorship = new StellarSponsorshipClient(config.sponsorship); } + this.admin = new StellarAdminClient(config); } async signSponsorshipAuthorization(build: StellarSponsorBuild): Promise { diff --git a/src/stellar/error-parser.ts b/src/stellar/error-parser.ts new file mode 100644 index 0000000..a8f8907 --- /dev/null +++ b/src/stellar/error-parser.ts @@ -0,0 +1,182 @@ +import { FUNDABLE_ERROR_CODES, FundableError } from "../core/index.js"; + +export interface SorobanContractErrorDefinition { + code: number; + contract: "flow" | "lockup" | "stream_nft" | "router" | "paymaster"; + name: string; + message: string; +} + +export const SOROBAN_CONTRACT_ERRORS: Record = { + // Flow Contract Errors (1-28) + 1: { code: 1, contract: "flow", name: "StreamNotFound", message: "Flow stream does not exist." }, + 2: { code: 2, contract: "flow", name: "StreamAlreadyVoided", message: "Flow stream has already been voided." }, + 3: { code: 3, contract: "flow", name: "StreamAlreadyDepleted", message: "Flow stream has already been depleted." }, + 4: { code: 4, contract: "flow", name: "StreamPaused", message: "Flow stream is paused." }, + 5: { code: 5, contract: "flow", name: "StreamNotPaused", message: "Flow stream is not paused." }, + 6: { code: 6, contract: "flow", name: "StreamNotStreaming", message: "Flow stream is not in an active streaming state." }, + 7: { code: 7, contract: "flow", name: "StreamNotPending", message: "Flow stream is not pending." }, + 8: { code: 8, contract: "flow", name: "StreamNotDepleted", message: "Flow stream is not depleted." }, + 9: { code: 9, contract: "flow", name: "InsufficientBalance", message: "Stream balance is insufficient for this withdrawal or refund." }, + 10: { code: 10, contract: "flow", name: "AmountExceedsBalance", message: "Requested amount exceeds stream balance." }, + 11: { code: 11, contract: "flow", name: "InvalidRate", message: "Flow rate per second must be greater than zero." }, + 12: { code: 12, contract: "flow", name: "InvalidDepositAmount", message: "Deposit amount must be greater than zero." }, + 13: { code: 13, contract: "flow", name: "InvalidWithdrawAmount", message: "Withdrawal amount must be greater than zero." }, + 14: { code: 14, contract: "flow", name: "InvalidRefundAmount", message: "Refund amount must be greater than zero." }, + 15: { code: 15, contract: "flow", name: "InvalidStartTime", message: "Start time must not be in the past." }, + 16: { code: 16, contract: "flow", name: "AlreadyInitialized", message: "Flow contract has already been initialized." }, + 17: { code: 17, contract: "flow", name: "NotInitialized", message: "Flow contract is not initialized." }, + 18: { code: 18, contract: "flow", name: "NotAuthorized", message: "Caller is not authorized to perform this Flow action." }, + 19: { code: 19, contract: "flow", name: "ZeroRateVoidedStream", message: "Cannot void a stream with zero rate." }, + 20: { code: 20, contract: "flow", name: "SenderEqualsRecipient", message: "Sender and recipient addresses cannot be the same." }, + 21: { code: 21, contract: "flow", name: "NegativeRate", message: "Rate per second must not be negative." }, + 22: { code: 22, contract: "flow", name: "TokenTransferMismatch", message: "Token transfer resulted in an unexpected balance change." }, + 23: { code: 23, contract: "flow", name: "TokenDecimalsMismatch", message: "Token decimals mismatch: caller-supplied decimals do not match the token contract." }, + 24: { code: 24, contract: "flow", name: "ArithmeticError", message: "Checked arithmetic operation failed (overflow or underflow)." }, + 25: { code: 25, contract: "flow", name: "AdminTransferPending", message: "An admin transfer is already pending." }, + 26: { code: 26, contract: "flow", name: "NoAdminTransferPending", message: "No admin transfer is pending to accept." }, + 27: { code: 27, contract: "flow", name: "UpgradeTimelocked", message: "Contract upgrade is timelocked and cannot be executed yet." }, + 28: { code: 28, contract: "flow", name: "NoUpgradeProposed", message: "No upgrade has been proposed." }, + + // Lockup Contract Errors (101-120) + 101: { code: 101, contract: "lockup", name: "StreamNotFound", message: "Lockup stream does not exist." }, + 102: { code: 102, contract: "lockup", name: "StreamNotCancelable", message: "Lockup stream is not cancelable." }, + 103: { code: 103, contract: "lockup", name: "StreamAlreadyRenounced", message: "Lockup stream cancellation rights have already been renounced." }, + 104: { code: 104, contract: "lockup", name: "InvalidTimeRange", message: "End time must be later than start time." }, + 105: { code: 105, contract: "lockup", name: "InvalidCliffTime", message: "Cliff time must be between start time and end time." }, + 106: { code: 106, contract: "lockup", name: "InvalidTotalAmount", message: "Total stream amount must be greater than zero." }, + 107: { code: 107, contract: "lockup", name: "InvalidGranularity", message: "Granularity must be greater than zero." }, + 108: { code: 108, contract: "lockup", name: "AlreadyInitialized", message: "Lockup contract has already been initialized." }, + 109: { code: 109, contract: "lockup", name: "NotInitialized", message: "Lockup contract is not initialized." }, + 110: { code: 110, contract: "lockup", name: "SenderEqualsRecipient", message: "Sender and recipient addresses cannot be the same." }, + 111: { code: 111, contract: "lockup", name: "NegativeUnlockAmount", message: "Lockup unlock amounts (start or cliff) cannot be negative." }, + 112: { code: 112, contract: "lockup", name: "UnlockSumOverflow", message: "Sum of start and cliff unlock amounts overflowed maximum integer capacity." }, + 113: { code: 113, contract: "lockup", name: "InvalidUnlockSum", message: "Sum of start and cliff unlock amounts cannot exceed total stream amount." }, + 114: { code: 114, contract: "lockup", name: "InvalidCancellationAmount", message: "Defensive cancellation amount validation failed." }, + 115: { code: 115, contract: "lockup", name: "TokenTransferMismatch", message: "Token transfer resulted in an unexpected balance change." }, + 116: { code: 116, contract: "lockup", name: "ArithmeticError", message: "Checked arithmetic operation failed (overflow or underflow)." }, + 117: { code: 117, contract: "lockup", name: "AdminTransferPending", message: "An admin transfer is already pending." }, + 118: { code: 118, contract: "lockup", name: "NoAdminTransferPending", message: "No admin transfer is pending to accept." }, + 119: { code: 119, contract: "lockup", name: "UpgradeTimelocked", message: "Contract upgrade is timelocked and cannot be executed yet." }, + 120: { code: 120, contract: "lockup", name: "NoUpgradeProposed", message: "No upgrade has been proposed." }, + + // Stream NFT Errors (201-205) + 201: { code: 201, contract: "stream_nft", name: "AlreadyInitialized", message: "Stream NFT contract has already been initialized." }, + 202: { code: 202, contract: "stream_nft", name: "NotAuthorized", message: "Caller is not authorized to perform this Stream NFT action." }, + 203: { code: 203, contract: "stream_nft", name: "TokenNotFound", message: "Stream NFT token not found." }, + 204: { code: 204, contract: "stream_nft", name: "NotTransferable", message: "Stream NFT is not transferable." }, + 205: { code: 205, contract: "stream_nft", name: "AlreadyMinted", message: "Stream NFT token ID has already been minted." }, + + // Router Contract Errors (301-305) + 301: { code: 301, contract: "router", name: "AlreadyInitialized", message: "Router contract has already been initialized." }, + 302: { code: 302, contract: "router", name: "NotInitialized", message: "Router contract is not initialized." }, + 303: { code: 303, contract: "router", name: "NotAuthorized", message: "Caller is not authorized to perform this Router action." }, + 304: { code: 304, contract: "router", name: "InvalidStreamType", message: "Invalid stream type specified for Router operation." }, + 305: { code: 305, contract: "router", name: "InvalidContractAddress", message: "A required contract address is invalid or unset on Router." }, + + // Paymaster Contract Errors (401-408) + 401: { code: 401, contract: "paymaster", name: "AlreadyInitialized", message: "Paymaster contract has already been initialized." }, + 402: { code: 402, contract: "paymaster", name: "NotInitialized", message: "Paymaster contract is not initialized." }, + 403: { code: 403, contract: "paymaster", name: "NotAuthorized", message: "Caller is not authorized to perform this Paymaster action." }, + 404: { code: 404, contract: "paymaster", name: "FeeTokenNotAllowed", message: "Fee token is not in the Paymaster allowlist." }, + 405: { code: 405, contract: "paymaster", name: "FeeExceedsMaximum", message: "Requested fee exceeds authorized maximum fee." }, + 406: { code: 406, contract: "paymaster", name: "ApprovalExpired", message: "Fee authorization has expired." }, + 407: { code: 407, contract: "paymaster", name: "CallFailed", message: "Forwarded contract call failed." }, + 408: { code: 408, contract: "paymaster", name: "InvalidFeeRecipient", message: "Invalid fee recipient address." }, +}; + +/** + * Extracts a numeric Soroban contract error code from an error or error message string. + * Supports patterns such as: + * - `Error(Contract, #111)` + * - `Error(Contract, 111)` + * - `HostError: Error(Contract, #113)` + * - `ContractError(#111)` or `ContractError: 111` + */ +export function parseSorobanErrorCode(error: unknown): number | undefined { + if (error === null || error === undefined) return undefined; + + // Direct error code property + if (typeof error === "object") { + const errorObj = error as Record; + if (typeof errorObj.contractCode === "number") { + return errorObj.contractCode; + } + if (typeof errorObj.code === "number" && errorObj.code in SOROBAN_CONTRACT_ERRORS) { + return errorObj.code; + } + } + + const message = + error instanceof Error + ? `${error.message} ${"cause" in error && error.cause instanceof Error ? error.cause.message : ""}` + : String(error); + + const patterns = [ + /Error\(Contract,\s*#?(\d+)\)/i, + /ContractError\s*[:(]\s*#?(\d+)\)?/i, + /contract\s+error\s*[:#]\s*(\d+)/i, + /HostError.*Error\(Contract,\s*#?(\d+)\)/i, + /\bcode\s*[:=]\s*#?(\d+)\b/i, + ]; + + for (const pattern of patterns) { + const match = message.match(pattern); + if (match?.[1]) { + const code = parseInt(match[1], 10); + if (Number.isInteger(code) && code in SOROBAN_CONTRACT_ERRORS) { + return code; + } + } + } + + return undefined; +} + +/** + * Translates an unknown error (typically from a Soroban simulation or RPC invocation) + * into a descriptive FundableError if it matches a known Soroban contract error. + */ +export function translateSorobanError( + error: unknown, + fallbackMessage: string, +): FundableError { + if (error instanceof FundableError) { + return error; + } + + const code = parseSorobanErrorCode(error); + if (code !== undefined) { + const def = SOROBAN_CONTRACT_ERRORS[code]; + if (def) { + return new FundableError({ + code: FUNDABLE_ERROR_CODES.SIMULATION_FAILED, + message: `${def.message} [${def.contract.toUpperCase()} error ${def.code}: ${def.name}]`, + chain: "stellar", + cause: error, + }); + } + } + + const rawMessage = error instanceof Error ? error.message : String(error); + return new FundableError({ + code: FUNDABLE_ERROR_CODES.SIMULATION_FAILED, + message: rawMessage || fallbackMessage, + chain: "stellar", + cause: error, + }); +} + +/** + * Standard error translation helper for Stellar Soroban contract calls. + */ +export function toFundableError( + error: unknown, + fallbackMessage: string, + chain: "stellar" = "stellar", +): FundableError { + if (error instanceof FundableError) { + return error; + } + return translateSorobanError(error, fallbackMessage); +} diff --git a/src/stellar/flow-client.ts b/src/stellar/flow-client.ts index 3eacee7..84f01e9 100644 --- a/src/stellar/flow-client.ts +++ b/src/stellar/flow-client.ts @@ -2,7 +2,6 @@ import type { AssembledTransaction } from "@stellar/stellar-sdk/contract"; import { CHAIN_FAMILIES, FLOW_STATUSES, - toFundableError, toUnixSeconds, type AdjustFlowRateInput, type CreateAndDepositFlowInput, @@ -14,6 +13,7 @@ import { type RestartFlowInput, type WithdrawFlowInput, } from "../core/index.js"; +import { toFundableError } from "./error-parser.js"; import { Client as GeneratedFlowClient, StreamStatus as GeneratedStreamStatus, @@ -59,17 +59,21 @@ export class StellarFlowClient { options?: StellarMethodOptions, ): Promise> { this.validateCreateInput(input); - return this.client.create( - { - sender: input.sender, - recipient: input.recipient, - token: input.token.address, - rate_per_second: input.ratePerSecond, - token_decimals: input.token.decimals, - start_time: toUnixSeconds(input.startTime), - }, - options, - ); + try { + return await this.client.create( + { + sender: input.sender, + recipient: input.recipient, + token: input.token.address, + rate_per_second: input.ratePerSecond, + token_decimals: input.token.decimals, + start_time: toUnixSeconds(input.startTime), + }, + options, + ); + } catch (error) { + throw toFundableError(error, "Failed to create Flow stream."); + } } async createAndDeposit( @@ -78,18 +82,22 @@ export class StellarFlowClient { ): Promise> { this.validateCreateInput(input); assertPositive(input.amount, "Deposit amount"); - return this.client.create_and_deposit( - { - sender: input.sender, - recipient: input.recipient, - token: input.token.address, - rate_per_second: input.ratePerSecond, - token_decimals: input.token.decimals, - start_time: toUnixSeconds(input.startTime), - amount: input.amount, - }, - options, - ); + try { + return await this.client.create_and_deposit( + { + sender: input.sender, + recipient: input.recipient, + token: input.token.address, + rate_per_second: input.ratePerSecond, + token_decimals: input.token.decimals, + start_time: toUnixSeconds(input.startTime), + amount: input.amount, + }, + options, + ); + } catch (error) { + throw toFundableError(error, "Failed to create and deposit Flow stream."); + } } async deposit( @@ -98,14 +106,18 @@ export class StellarFlowClient { ): Promise> { assertStellarAddress(input.funder, "Funder"); assertPositive(input.amount, "Deposit amount"); - return this.client.deposit( - { - stream_id: toStreamId(input.streamId), - funder: input.funder, - amount: input.amount, - }, - options, - ); + try { + return await this.client.deposit( + { + stream_id: toStreamId(input.streamId), + funder: input.funder, + amount: input.amount, + }, + options, + ); + } catch (error) { + throw toFundableError(error, "Failed to deposit into Flow stream."); + } } async withdraw( @@ -115,15 +127,19 @@ export class StellarFlowClient { assertStellarAddress(input.caller, "Caller"); assertStellarAddress(input.to, "Withdrawal destination"); assertPositive(input.amount, "Withdrawal amount"); - return this.client.withdraw( - { - stream_id: toStreamId(input.streamId), - caller: input.caller, - to: input.to, - amount: input.amount, - }, - options, - ); + try { + return await this.client.withdraw( + { + stream_id: toStreamId(input.streamId), + caller: input.caller, + to: input.to, + amount: input.amount, + }, + options, + ); + } catch (error) { + throw toFundableError(error, "Failed to withdraw from Flow stream."); + } } async withdrawMax( @@ -132,14 +148,18 @@ export class StellarFlowClient { ): Promise> { assertStellarAddress(input.actor, "Caller"); assertStellarAddress(input.to, "Withdrawal destination"); - return this.client.withdraw_max( - { - stream_id: toStreamId(input.streamId), - caller: input.actor, - to: input.to, - }, - options, - ); + try { + return await this.client.withdraw_max( + { + stream_id: toStreamId(input.streamId), + caller: input.actor, + to: input.to, + }, + options, + ); + } catch (error) { + throw toFundableError(error, "Failed to withdraw maximum from Flow stream."); + } } async pause( @@ -147,13 +167,17 @@ export class StellarFlowClient { options?: StellarMethodOptions, ): Promise> { assertStellarAddress(input.actor, "Sender"); - return this.client.pause( - { - stream_id: toStreamId(input.streamId), - sender: input.actor, - }, - options, - ); + try { + return await this.client.pause( + { + stream_id: toStreamId(input.streamId), + sender: input.actor, + }, + options, + ); + } catch (error) { + throw toFundableError(error, "Failed to pause Flow stream."); + } } async restart( @@ -162,14 +186,18 @@ export class StellarFlowClient { ): Promise> { assertStellarAddress(input.actor, "Sender"); assertPositive(input.ratePerSecond, "Rate per second"); - return this.client.restart( - { - stream_id: toStreamId(input.streamId), - sender: input.actor, - rate_per_second: input.ratePerSecond, - }, - options, - ); + try { + return await this.client.restart( + { + stream_id: toStreamId(input.streamId), + sender: input.actor, + rate_per_second: input.ratePerSecond, + }, + options, + ); + } catch (error) { + throw toFundableError(error, "Failed to restart Flow stream."); + } } async adjustRate( @@ -178,14 +206,18 @@ export class StellarFlowClient { ): Promise> { assertStellarAddress(input.actor, "Sender"); assertPositive(input.ratePerSecond, "Rate per second"); - return this.client.adjust_rate( - { - stream_id: toStreamId(input.streamId), - sender: input.actor, - new_rate: input.ratePerSecond, - }, - options, - ); + try { + return await this.client.adjust_rate( + { + stream_id: toStreamId(input.streamId), + sender: input.actor, + new_rate: input.ratePerSecond, + }, + options, + ); + } catch (error) { + throw toFundableError(error, "Failed to adjust Flow rate."); + } } async refund( @@ -194,14 +226,18 @@ export class StellarFlowClient { ): Promise> { assertStellarAddress(input.actor, "Sender"); assertPositive(input.amount, "Refund amount"); - return this.client.refund( - { - stream_id: toStreamId(input.streamId), - sender: input.actor, - amount: input.amount, - }, - options, - ); + try { + return await this.client.refund( + { + stream_id: toStreamId(input.streamId), + sender: input.actor, + amount: input.amount, + }, + options, + ); + } catch (error) { + throw toFundableError(error, "Failed to refund Flow stream."); + } } async refundMax( @@ -209,13 +245,17 @@ export class StellarFlowClient { options?: StellarMethodOptions, ): Promise> { assertStellarAddress(input.actor, "Sender"); - return this.client.refund_max( - { - stream_id: toStreamId(input.streamId), - sender: input.actor, - }, - options, - ); + try { + return await this.client.refund_max( + { + stream_id: toStreamId(input.streamId), + sender: input.actor, + }, + options, + ); + } catch (error) { + throw toFundableError(error, "Failed to refund maximum from Flow stream."); + } } async void( @@ -223,15 +263,34 @@ export class StellarFlowClient { options?: StellarMethodOptions, ): Promise> { assertStellarAddress(input.actor, "Caller"); - return this.client.void_stream( - { - stream_id: toStreamId(input.streamId), - caller: input.actor, - }, - options, - ); + try { + return await this.client.void_stream( + { + stream_id: toStreamId(input.streamId), + caller: input.actor, + }, + options, + ); + } catch (error) { + throw toFundableError(error, "Failed to void Flow stream."); + } } + async extendStreamTtl( + streamId: string | bigint, + options?: StellarMethodOptions, + ): Promise> { + try { + return await this.client.extend_stream_ttl( + { stream_id: toStreamId(streamId) }, + options, + ); + } catch (error) { + throw toFundableError(error, "Failed to extend Flow stream TTL."); + } + } + + async getStream(streamId: string | bigint): Promise { const id = toStreamId(streamId); try { diff --git a/src/stellar/index.ts b/src/stellar/index.ts index 595a129..ea4642e 100644 --- a/src/stellar/index.ts +++ b/src/stellar/index.ts @@ -1,4 +1,12 @@ +export * from "./admin-client.js"; export * from "./client.js"; +export { + SOROBAN_CONTRACT_ERRORS, + parseSorobanErrorCode, + translateSorobanError, + toFundableError as toStellarFundableError, + type SorobanContractErrorDefinition, +} from "./error-parser.js"; export * from "./flow-client.js"; export * from "./lockup-client.js"; export * from "./paymaster-client.js"; @@ -6,3 +14,4 @@ export * from "./router-client.js"; export * from "./stream-nft-client.js"; export * from "./sponsorship-client.js"; export * from "./types.js"; +export * from "./validation.js"; diff --git a/src/stellar/lockup-client.ts b/src/stellar/lockup-client.ts index 20b2c1a..2bef997 100644 --- a/src/stellar/lockup-client.ts +++ b/src/stellar/lockup-client.ts @@ -1,9 +1,9 @@ import { LOCKUP_STATUSES, - toFundableError, type LockupActorInput, type LockupStatus, } from "../core/index.js"; +import { toFundableError } from "./error-parser.js"; import { Client as GeneratedLockupClient, LockupStatus as GeneratedLockupStatus, @@ -46,10 +46,14 @@ export class StellarLockupClient { options?: StellarMethodOptions, ): Promise> { assertStellarAddress(input.sender, "Lockup sender"); - return this.client.cancel( - { stream_id: toStreamId(input.streamId), sender: input.sender }, - options, - ); + try { + return await this.client.cancel( + { stream_id: toStreamId(input.streamId), sender: input.sender }, + options, + ); + } catch (error) { + throw toFundableError(error, "Failed to cancel Lockup stream."); + } } async renounce( @@ -57,10 +61,28 @@ export class StellarLockupClient { options?: StellarMethodOptions, ): Promise> { assertStellarAddress(input.sender, "Lockup sender"); - return this.client.renounce( - { stream_id: toStreamId(input.streamId), sender: input.sender }, - options, - ); + try { + return await this.client.renounce( + { stream_id: toStreamId(input.streamId), sender: input.sender }, + options, + ); + } catch (error) { + throw toFundableError(error, "Failed to renounce Lockup stream."); + } + } + + async extendStreamTtl( + streamId: string | bigint, + options?: StellarMethodOptions, + ): Promise> { + try { + return await this.client.extend_stream_ttl( + { stream_id: toStreamId(streamId) }, + options, + ); + } catch (error) { + throw toFundableError(error, "Failed to extend Lockup stream TTL."); + } } async statusOf(streamId: string | bigint): Promise { diff --git a/src/stellar/paymaster-client.ts b/src/stellar/paymaster-client.ts index 83b78ec..04ce0f9 100644 --- a/src/stellar/paymaster-client.ts +++ b/src/stellar/paymaster-client.ts @@ -1,9 +1,9 @@ import { FUNDABLE_ERROR_CODES, FundableError, - toFundableError, type PaymasterForwardInput, } from "../core/index.js"; +import { toFundableError } from "./error-parser.js"; import { Client as GeneratedPaymasterClient } from "../generated/paymaster/src/index.js"; import type { StellarFundableClientConfig, StellarMethodOptions } from "./types.js"; import type { StellarTransaction } from "./flow-client.js"; diff --git a/src/stellar/protocol-clients.test.ts b/src/stellar/protocol-clients.test.ts index 0c40e3e..b2e2963 100644 --- a/src/stellar/protocol-clients.test.ts +++ b/src/stellar/protocol-clients.test.ts @@ -6,27 +6,35 @@ const generated = vi.hoisted(() => ({ createFlow: vi.fn(), createLockup: vi.fn(), getStreamData: vi.fn(), - getStream: vi.fn(), - isTransferable: vi.fn(), + ownerOf: vi.fn(), cancelLockup: vi.fn(), lockupStatus: vi.fn(), forward: vi.fn(), + flowProposeUpgrade: vi.fn(), + flowExecuteUpgrade: vi.fn(), + flowUpgrade: vi.fn(), + flowProposeAdmin: vi.fn(), + flowAcceptAdmin: vi.fn(), + flowExtendStreamTtl: vi.fn(), + routerUpgradeNft: vi.fn(), + routerProposeUpgrade: vi.fn(), + routerExecuteUpgrade: vi.fn(), + routerUpgrade: vi.fn(), + routerProposeAdmin: vi.fn(), + routerAcceptAdmin: vi.fn(), })); vi.mock("../generated/router/src/index.js", () => ({ - CanonicalStreamStatus: { - Pending: 0, - Active: 1, - Paused: 2, - Canceled: 3, - Completed: 4, - Failed: 5, - }, StreamType: { Flow: 0, Lockup: 1 }, Client: class { create_flow_stream = generated.createFlow; create_lockup_stream = generated.createLockup; - get_stream = generated.getStream; + upgrade_nft = generated.routerUpgradeNft; + propose_upgrade = generated.routerProposeUpgrade; + execute_upgrade = generated.routerExecuteUpgrade; + upgrade = generated.routerUpgrade; + propose_admin = generated.routerProposeAdmin; + accept_admin = generated.routerAcceptAdmin; }, })); @@ -34,7 +42,26 @@ vi.mock("../generated/stream_nft/src/index.js", () => ({ StreamType: { Flow: 0, Lockup: 1 }, Client: class { get_stream_data = generated.getStreamData; - is_transferable = generated.isTransferable; + owner_of = generated.ownerOf; + }, +})); + +vi.mock("../generated/flow/src/index.js", () => ({ + StreamStatus: { + Pending: 0, + StreamingSolvent: 1, + StreamingInsolvent: 2, + PausedSolvent: 3, + PausedInsolvent: 4, + Voided: 5, + }, + Client: class { + propose_upgrade = generated.flowProposeUpgrade; + execute_upgrade = generated.flowExecuteUpgrade; + upgrade = generated.flowUpgrade; + propose_admin = generated.flowProposeAdmin; + accept_admin = generated.flowAcceptAdmin; + extend_stream_ttl = generated.flowExtendStreamTtl; }, })); @@ -58,10 +85,13 @@ vi.mock("../generated/lockup/src/index.js", () => ({ }, })); -import { StellarPaymasterClient } from "./paymaster-client.js"; +import { StellarAdminClient } from "./admin-client.js"; +import { parseSorobanErrorCode, translateSorobanError } from "./error-parser.js"; import { StellarLockupClient } from "./lockup-client.js"; +import { StellarPaymasterClient } from "./paymaster-client.js"; import { StellarRouterClient } from "./router-client.js"; import { StellarStreamNftClient } from "./stream-nft-client.js"; +import { assertLockupUnlockAmounts, assertTokenDecimalsMatch } from "./validation.js"; function contractId(seed = 0): string { return StrKey.encodeContract(Buffer.alloc(32, seed)); @@ -106,8 +136,6 @@ describe("Stellar protocol clients", () => { rate_per_second: 10n ** 18n, token_decimals: 7, start_time: 123n, - initial_amount: 0n, - transferable: false, }, undefined, ); @@ -153,7 +181,6 @@ describe("Stellar protocol clients", () => { granularity: 60n, cancelable: true, }, - transferable: true, }, undefined, ); @@ -179,6 +206,16 @@ describe("Stellar protocol clients", () => { expect(generated.createLockup).not.toHaveBeenCalled(); }); + it("validates Lockup unlock amounts and token decimals client-side", () => { + expect(() => assertLockupUnlockAmounts(-1n, 0n, 100n)).toThrow("cannot be negative"); + expect(() => assertLockupUnlockAmounts(0n, -5n, 100n)).toThrow("cannot be negative"); + expect(() => assertLockupUnlockAmounts(60n, 50n, 100n)).toThrow("cannot exceed total stream amount"); + expect(() => assertLockupUnlockAmounts(50n, 50n, 100n)).not.toThrow(); + + expect(() => assertTokenDecimalsMatch(7, 6)).toThrow("Token decimals mismatch"); + expect(() => assertTokenDecimalsMatch(7, 7)).not.toThrow(); + }); + it("normalizes Stream NFT metadata", async () => { generated.getStreamData.mockResolvedValue({ result: [0, 42n] }); const client = new StellarStreamNftClient({ @@ -195,19 +232,17 @@ describe("Stellar protocol clients", () => { it("loads canonical Router metadata by public NFT token ID", async () => { const owner = Keypair.random().publicKey(); - generated.getStream.mockResolvedValue({ - result: { - token_id: 7n, - core_stream_id: 42n, - stream_type: 1, - status: 3, - owner, - transferable: true, - }, - }); + generated.ownerOf.mockResolvedValue({ result: owner }); + generated.getStreamData.mockResolvedValue({ result: [1, 42n] }); + generated.lockupStatus.mockResolvedValue({ result: 3 }); const client = new StellarRouterClient({ ...baseConfig, - contracts: { ...baseConfig.contracts, router: contractId(1) }, + contracts: { + ...baseConfig.contracts, + router: contractId(1), + streamNft: contractId(3), + lockup: contractId(4), + }, }); await expect(client.getStream("7")).resolves.toEqual({ @@ -221,13 +256,14 @@ describe("Stellar protocol clients", () => { }); it("reads immutable NFT transferability", async () => { - generated.isTransferable.mockResolvedValue({ result: false }); + const owner = Keypair.random().publicKey(); + generated.ownerOf.mockResolvedValue({ result: owner }); const client = new StellarStreamNftClient({ ...baseConfig, contracts: { ...baseConfig.contracts, streamNft: contractId(3) }, }); - await expect(client.isTransferable("7")).resolves.toBe(false); + await expect(client.isTransferable("7")).resolves.toBe(true); }); it("routes sender cancellation through the Lockup core contract", async () => { @@ -285,4 +321,88 @@ describe("Stellar protocol clients", () => { undefined, ); }); + + it("routes administrative maintenance and upgrades", async () => { + const dummyHash = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + const expectedBuffer = Buffer.from(dummyHash, "hex"); + const newAdmin = Keypair.random().publicKey(); + + generated.flowProposeUpgrade.mockResolvedValue({ result: null }); + generated.flowExecuteUpgrade.mockResolvedValue({ result: null }); + generated.flowProposeAdmin.mockResolvedValue({ result: null }); + generated.flowAcceptAdmin.mockResolvedValue({ result: null }); + generated.flowExtendStreamTtl.mockResolvedValue({ result: null }); + generated.routerUpgradeNft.mockResolvedValue({ result: null }); + + const adminClient = new StellarAdminClient({ + ...baseConfig, + contracts: { + ...baseConfig.contracts, + router: contractId(1), + }, + }); + + await adminClient.flows.proposeUpgrade(dummyHash); + expect(generated.flowProposeUpgrade).toHaveBeenCalledWith( + { new_wasm_hash: expectedBuffer }, + undefined, + ); + + await adminClient.flows.executeUpgrade(dummyHash); + expect(generated.flowExecuteUpgrade).toHaveBeenCalledWith( + { new_wasm_hash: expectedBuffer }, + undefined, + ); + + await adminClient.flows.proposeAdmin(newAdmin); + expect(generated.flowProposeAdmin).toHaveBeenCalledWith( + { new_admin: newAdmin }, + undefined, + ); + + await adminClient.flows.acceptAdmin(); + expect(generated.flowAcceptAdmin).toHaveBeenCalledWith(undefined); + + await adminClient.flows.extendStreamTtl(10n); + expect(generated.flowExtendStreamTtl).toHaveBeenCalledWith( + { stream_id: 10n }, + undefined, + ); + + await adminClient.router?.upgradeNft(dummyHash); + expect(generated.routerUpgradeNft).toHaveBeenCalledWith( + { new_wasm_hash: expectedBuffer }, + undefined, + ); + }); + + it("parses and translates hardened Soroban contract errors accurately", () => { + // Lockup 111 is now NegativeUnlockAmount + expect(parseSorobanErrorCode("HostError: Error(Contract, #111)")).toBe(111); + const err111 = translateSorobanError( + new Error("HostError: Error(Contract, #111)"), + "Fallback", + ); + expect(err111.message).toContain("Lockup unlock amounts"); + expect(err111.message).toContain("cannot be negative"); + expect(err111.message).toContain("NegativeUnlockAmount"); + + // Flow 22 TokenTransferMismatch + expect(parseSorobanErrorCode("Error(Contract, 22)")).toBe(22); + const err22 = translateSorobanError( + new Error("Error(Contract, 22)"), + "Fallback", + ); + expect(err22.message).toContain("Token transfer resulted in an unexpected balance change"); + expect(err22.message).toContain("TokenTransferMismatch"); + + // Router 305 InvalidContractAddress + expect(parseSorobanErrorCode("Error(Contract, #305)")).toBe(305); + const err305 = translateSorobanError( + new Error("Error(Contract, #305)"), + "Fallback", + ); + expect(err305.message).toContain("A required contract address is invalid"); + expect(err305.message).toContain("InvalidContractAddress"); + }); }); diff --git a/src/stellar/router-client.ts b/src/stellar/router-client.ts index fa96ded..bc823f7 100644 --- a/src/stellar/router-client.ts +++ b/src/stellar/router-client.ts @@ -3,7 +3,6 @@ import { FundableError, CANONICAL_STREAM_STATUSES, STREAM_KINDS, - toFundableError, toUnixSeconds, type CreateFlowInput, type CreateLockupInput, @@ -11,32 +10,53 @@ import { type CanonicalStreamStatus, type StreamMetadata, } from "../core/index.js"; +import { toFundableError } from "./error-parser.js"; +import { Client as GeneratedRouterClient } from "../generated/router/src/index.js"; import { - CanonicalStreamStatus as GeneratedCanonicalStreamStatus, - Client as GeneratedRouterClient, + Client as GeneratedStreamNftClient, StreamType as GeneratedStreamType, -} from "../generated/router/src/index.js"; +} from "../generated/stream_nft/src/index.js"; +import { + Client as GeneratedFlowClient, + StreamStatus as GeneratedFlowStatus, +} from "../generated/flow/src/index.js"; +import { + Client as GeneratedLockupClient, + LockupStatus as GeneratedLockupStatus, +} from "../generated/lockup/src/index.js"; import type { StellarFundableClientConfig, StellarMethodOptions } from "./types.js"; import type { StellarTransaction } from "./flow-client.js"; import { assertPositive, - assertNonNegative, + assertLockupUnlockAmounts, assertStellarAddress, assertTokenDecimals, + toStreamId, toTokenId, } from "./validation.js"; -const CANONICAL_STATUS_BY_CONTRACT_VALUE: Record = { - [GeneratedCanonicalStreamStatus.Pending]: CANONICAL_STREAM_STATUSES.PENDING, - [GeneratedCanonicalStreamStatus.Active]: CANONICAL_STREAM_STATUSES.ACTIVE, - [GeneratedCanonicalStreamStatus.Paused]: CANONICAL_STREAM_STATUSES.PAUSED, - [GeneratedCanonicalStreamStatus.Canceled]: CANONICAL_STREAM_STATUSES.CANCELED, - [GeneratedCanonicalStreamStatus.Completed]: CANONICAL_STREAM_STATUSES.COMPLETED, - [GeneratedCanonicalStreamStatus.Failed]: CANONICAL_STREAM_STATUSES.FAILED, +const CANONICAL_FLOW_STATUS: Record = { + [GeneratedFlowStatus.Pending]: CANONICAL_STREAM_STATUSES.PENDING, + [GeneratedFlowStatus.StreamingSolvent]: CANONICAL_STREAM_STATUSES.ACTIVE, + [GeneratedFlowStatus.StreamingInsolvent]: CANONICAL_STREAM_STATUSES.ACTIVE, + [GeneratedFlowStatus.PausedSolvent]: CANONICAL_STREAM_STATUSES.PAUSED, + [GeneratedFlowStatus.PausedInsolvent]: CANONICAL_STREAM_STATUSES.PAUSED, + [GeneratedFlowStatus.Voided]: CANONICAL_STREAM_STATUSES.CANCELED, +}; + +const CANONICAL_LOCKUP_STATUS: Record = { + [GeneratedLockupStatus.Pending]: CANONICAL_STREAM_STATUSES.PENDING, + [GeneratedLockupStatus.Streaming]: CANONICAL_STREAM_STATUSES.ACTIVE, + [GeneratedLockupStatus.Settled]: CANONICAL_STREAM_STATUSES.COMPLETED, + [GeneratedLockupStatus.Canceled]: CANONICAL_STREAM_STATUSES.CANCELED, + [GeneratedLockupStatus.Depleted]: CANONICAL_STREAM_STATUSES.COMPLETED, }; export class StellarRouterClient { private readonly client: GeneratedRouterClient; + private readonly streamNftClient?: GeneratedStreamNftClient; + private readonly flowClient?: GeneratedFlowClient; + private readonly lockupClient?: GeneratedLockupClient; constructor(config: StellarFundableClientConfig & { contracts: { router: string } }) { this.client = new GeneratedRouterClient({ @@ -49,6 +69,45 @@ export class StellarRouterClient { signTransaction: config.signTransaction, signAuthEntry: config.signAuthEntry, }); + + if (config.contracts.streamNft) { + this.streamNftClient = new GeneratedStreamNftClient({ + contractId: config.contracts.streamNft, + networkPassphrase: config.networkPassphrase, + rpcUrl: config.rpcUrl, + publicKey: config.publicKey, + allowHttp: config.allowHttp, + headers: config.headers, + signTransaction: config.signTransaction, + signAuthEntry: config.signAuthEntry, + }); + } + + if (config.contracts.flow) { + this.flowClient = new GeneratedFlowClient({ + contractId: config.contracts.flow, + networkPassphrase: config.networkPassphrase, + rpcUrl: config.rpcUrl, + publicKey: config.publicKey, + allowHttp: config.allowHttp, + headers: config.headers, + signTransaction: config.signTransaction, + signAuthEntry: config.signAuthEntry, + }); + } + + if (config.contracts.lockup) { + this.lockupClient = new GeneratedLockupClient({ + contractId: config.contracts.lockup, + networkPassphrase: config.networkPassphrase, + rpcUrl: config.rpcUrl, + publicKey: config.publicKey, + allowHttp: config.allowHttp, + headers: config.headers, + signTransaction: config.signTransaction, + signAuthEntry: config.signAuthEntry, + }); + } } async createFlow( @@ -61,19 +120,21 @@ export class StellarRouterClient { assertPositive(input.ratePerSecond, "Rate per second"); assertTokenDecimals(input.token.decimals, "Flow token decimals"); - return this.client.create_flow_stream( - { - sender: input.sender, - recipient: input.recipient, - token: input.token.address, - rate_per_second: input.ratePerSecond, - token_decimals: input.token.decimals, - start_time: toUnixSeconds(input.startTime), - initial_amount: input.initialAmount ?? 0n, - transferable: input.transferable ?? false, - }, - options, - ); + try { + return await this.client.create_flow_stream( + { + sender: input.sender, + recipient: input.recipient, + token: input.token.address, + rate_per_second: input.ratePerSecond, + token_decimals: input.token.decimals, + start_time: toUnixSeconds(input.startTime), + }, + options, + ); + } catch (error) { + throw toFundableError(error, "Failed to create Flow stream via Router."); + } } async createLockup( @@ -115,36 +176,31 @@ export class StellarRouterClient { chain: "stellar", }); } - assertNonNegative(startUnlockAmount, "Start unlock amount"); - assertNonNegative(cliffUnlockAmount, "Cliff unlock amount"); assertPositive(granularity, "Granularity"); - if (startUnlockAmount + cliffUnlockAmount > input.totalAmount) { - throw new FundableError({ - code: FUNDABLE_ERROR_CODES.INVALID_ARGUMENT, - message: "Start and cliff unlock amounts cannot exceed the total amount.", - chain: "stellar", - }); - } + assertLockupUnlockAmounts(startUnlockAmount, cliffUnlockAmount, input.totalAmount); - return this.client.create_lockup_stream( - { - params: { - sender: input.sender, - recipient: input.recipient, - token: input.token.address, - total_amount: input.totalAmount, - start_time: startTime, - end_time: endTime, - cliff_time: cliffTime, - start_unlock_amount: startUnlockAmount, - cliff_unlock_amount: cliffUnlockAmount, - granularity, - cancelable: input.cancelable ?? false, + try { + return await this.client.create_lockup_stream( + { + params: { + sender: input.sender, + recipient: input.recipient, + token: input.token.address, + total_amount: input.totalAmount, + start_time: startTime, + end_time: endTime, + cliff_time: cliffTime, + start_unlock_amount: startUnlockAmount, + cliff_unlock_amount: cliffUnlockAmount, + granularity, + cancelable: input.cancelable ?? false, + }, }, - transferable: input.transferable ?? false, - }, - options, - ); + options, + ); + } catch (error) { + throw toFundableError(error, "Failed to create Lockup stream via Router."); + } } async withdraw( @@ -154,15 +210,19 @@ export class StellarRouterClient { assertStellarAddress(input.caller, "Caller"); assertStellarAddress(input.to, "Withdrawal destination"); assertPositive(input.amount, "Withdrawal amount"); - return this.client.withdraw( - { - token_id: toTokenId(input.tokenId), - caller: input.caller, - to: input.to, - amount: input.amount, - }, - options, - ); + try { + return await this.client.withdraw( + { + token_id: toTokenId(input.tokenId), + caller: input.caller, + to: input.to, + amount: input.amount, + }, + options, + ); + } catch (error) { + throw toFundableError(error, "Failed to withdraw from stream via Router."); + } } async withdrawMax( @@ -171,71 +231,126 @@ export class StellarRouterClient { ): Promise> { assertStellarAddress(input.caller, "Caller"); assertStellarAddress(input.to, "Withdrawal destination"); - return this.client.withdraw_max( - { - token_id: toTokenId(input.tokenId), - caller: input.caller, - to: input.to, - }, - options, - ); + try { + return await this.client.withdraw_max( + { + token_id: toTokenId(input.tokenId), + caller: input.caller, + to: input.to, + }, + options, + ); + } catch (error) { + throw toFundableError(error, "Failed to withdraw maximum from stream via Router."); + } + } + + private ensureStreamNftClient(): GeneratedStreamNftClient { + if (!this.streamNftClient) { + throw new FundableError({ + code: FUNDABLE_ERROR_CODES.INVALID_CONFIGURATION, + message: + "Stream NFT contract address must be provided in client configuration to query NFT streams.", + chain: "stellar", + }); + } + return this.streamNftClient; } async ownerOf(tokenId: string | bigint): Promise { + const nftClient = this.ensureStreamNftClient(); try { - const transaction = await this.client.owner_of({ token_id: toTokenId(tokenId) }); + const transaction = await nftClient.owner_of({ token_id: toTokenId(tokenId) }); return transaction.result; } catch (error) { throw toFundableError(error, "Failed to load the current stream owner.", "stellar"); } } - async statusOf(tokenId: string | bigint): Promise { + private async getStreamData( + tokenId: string | bigint, + ): Promise { + const nftClient = this.ensureStreamNftClient(); try { - const transaction = await this.client.status_of({ token_id: toTokenId(tokenId) }); - const status = CANONICAL_STATUS_BY_CONTRACT_VALUE[transaction.result]; - if (!status) throw new Error(`Unknown canonical stream status: ${transaction.result}`); - return status; + const transaction = await nftClient.get_stream_data({ + token_id: toTokenId(tokenId), + }); + return transaction.result; } catch (error) { - throw toFundableError(error, "Failed to load the canonical stream status.", "stellar"); + throw toFundableError(error, "Failed to load stream data from NFT.", "stellar"); } } async coreStreamId(tokenId: string | bigint): Promise { + const [, coreId] = await this.getStreamData(tokenId); + return coreId.toString(); + } + + async statusOf(tokenId: string | bigint): Promise { + const [streamType, coreId] = await this.getStreamData(tokenId); try { - const transaction = await this.client.core_stream_id({ token_id: toTokenId(tokenId) }); - return transaction.result.toString(); + if (streamType === GeneratedStreamType.Flow) { + if (!this.flowClient) { + throw new FundableError({ + code: FUNDABLE_ERROR_CODES.INVALID_CONFIGURATION, + message: + "Flow contract address must be provided in client configuration to query Flow status.", + chain: "stellar", + }); + } + const tx = await this.flowClient.status_of({ stream_id: coreId }); + const status = CANONICAL_FLOW_STATUS[tx.result]; + if (!status) throw new Error(`Unknown Flow status: ${tx.result}`); + return status; + } + + if (streamType === GeneratedStreamType.Lockup) { + if (!this.lockupClient) { + throw new FundableError({ + code: FUNDABLE_ERROR_CODES.INVALID_CONFIGURATION, + message: + "Lockup contract address must be provided in client configuration to query Lockup status.", + chain: "stellar", + }); + } + const tx = await this.lockupClient.status_of({ stream_id: coreId }); + const status = CANONICAL_LOCKUP_STATUS[tx.result]; + if (!status) throw new Error(`Unknown Lockup status: ${tx.result}`); + return status; + } + + throw new Error(`Unknown stream type: ${streamType}`); } catch (error) { - throw toFundableError(error, "Failed to load the core stream ID.", "stellar"); + throw toFundableError(error, "Failed to load the canonical stream status.", "stellar"); } } async getStream(tokenId: string | bigint): Promise { - try { - const transaction = await this.client.get_stream({ token_id: toTokenId(tokenId) }); - const metadata = transaction.result; - const status = CANONICAL_STATUS_BY_CONTRACT_VALUE[metadata.status]; - if (!status) throw new Error(`Unknown canonical stream status: ${metadata.status}`); - if ( - metadata.stream_type !== GeneratedStreamType.Flow && - metadata.stream_type !== GeneratedStreamType.Lockup - ) { - throw new Error(`Unknown stream type: ${metadata.stream_type}`); - } - return { - tokenId: metadata.token_id.toString(), - coreStreamId: metadata.core_stream_id.toString(), - streamKind: - metadata.stream_type === GeneratedStreamType.Flow - ? STREAM_KINDS.FLOW - : STREAM_KINDS.LOCKUP, - status, - owner: metadata.owner, - transferable: metadata.transferable, - }; - } catch (error) { - throw toFundableError(error, "Failed to load stream metadata.", "stellar"); + const id = toTokenId(tokenId); + const [owner, [streamType, coreId], status] = await Promise.all([ + this.ownerOf(id), + this.getStreamData(id), + this.statusOf(id), + ]); + + if ( + streamType !== GeneratedStreamType.Flow && + streamType !== GeneratedStreamType.Lockup + ) { + throw new Error(`Unknown stream type: ${streamType}`); } + + return { + tokenId: id.toString(), + coreStreamId: coreId.toString(), + streamKind: + streamType === GeneratedStreamType.Flow + ? STREAM_KINDS.FLOW + : STREAM_KINDS.LOCKUP, + status, + owner, + transferable: true, + }; } async voidFlow( @@ -243,9 +358,21 @@ export class StellarRouterClient { options?: StellarMethodOptions, ): Promise> { assertStellarAddress(input.caller, "Caller"); - return this.client.void_flow( - { token_id: toTokenId(input.tokenId), caller: input.caller }, - options, - ); + if (!this.flowClient) { + throw new FundableError({ + code: FUNDABLE_ERROR_CODES.INVALID_CONFIGURATION, + message: "Flow contract address must be provided in client configuration.", + chain: "stellar", + }); + } + const coreId = await this.coreStreamId(input.tokenId); + try { + return await this.flowClient.void_stream( + { stream_id: toStreamId(coreId), caller: input.caller }, + options, + ); + } catch (error) { + throw toFundableError(error, "Failed to void Flow stream."); + } } } diff --git a/src/stellar/sponsorship-client.test.ts b/src/stellar/sponsorship-client.test.ts index c32cfbc..f26fd93 100644 --- a/src/stellar/sponsorship-client.test.ts +++ b/src/stellar/sponsorship-client.test.ts @@ -181,4 +181,101 @@ describe("StellarSponsorshipClient", () => { }); expect(fetcher).not.toHaveBeenCalled(); }); + + it("executes the complete sponsored Router Lockup creation flow", async () => { + const fetcher = vi + .fn() + // 1. Challenge + .mockResolvedValueOnce( + successfulResponse({ + challenge_id: "challenge-123", + message: "Sign this Fundable challenge", + expires_at: "2030-01-01T00:00:00.000Z", + }), + ) + // 2. Session verify + .mockResolvedValueOnce( + successfulResponse({ + access_token: "session-abc", + token_type: "Bearer", + expires_at: "2030-01-01T00:15:00.000Z", + wallet: { address: "GUSER", network: "TESTNET" }, + }), + ) + // 3. Quote + .mockResolvedValueOnce( + successfulResponse({ + fee_token: "CFEE", + fee_in_token: "5000000", + fee_in_token_ui: "0.5", + conversion_rate: "1", + max_fee_in_token: "5500000", + max_fee_in_token_ui: "0.55", + }), + ) + // 4. Build + .mockResolvedValueOnce( + successfulResponse({ + transaction_xdr: "sponsored-tx-xdr", + user_auth_entry: "raw-auth-entry-xdr", + fee_token: "CFEE", + fee_in_stroops: "100", + fee_in_token: "5000000", + fee_in_token_ui: "0.5", + max_fee_in_token: "5500000", + max_fee_in_token_ui: "0.55", + valid_until: "2030-01-01T00:00:00.000Z", + }), + ) + // 5. Submit + .mockResolvedValueOnce( + successfulResponse({ + intentId: "intent-router-lockup-1", + submissionId: "submission-99", + status: "pending", + relayerTransactionId: "relayer-tx-88", + transactionHash: "tx-hash-lockup-router", + streamId: "101", + reused: false, + }), + ); + + const client = new StellarSponsorshipClient({ + backendUrl: "https://api.example.com", + fetch: fetcher, + }); + + const session = await client.authenticate( + "GUSER", + "TESTNET", + async () => "wallet-sig", + ); + expect(session.accessToken).toBe("session-abc"); + + const unsignedRouterLockupXdr = "AAAA...unsigned-router-lockup-xdr..."; + const quote = await client.quote(unsignedRouterLockupXdr, "TESTNET"); + expect(quote.maximumFeeUi).toBe("0.55"); + + const build = await client.build(unsignedRouterLockupXdr, "TESTNET"); + expect(build.userAuthEntry).toBe("raw-auth-entry-xdr"); + + // Sign user auth entry + const signedAuthEntry = "AAAA...signed-user-auth-entry..."; + const submission = await client.submit({ + build, + signedAuthEntry, + network: "TESTNET", + intent: { + operation: "create_lockup", + contract: "CROUTER", + sender: "GUSER", + }, + idempotencyKey: "lockup-create-key-1", + }); + + expect(submission.streamTokenId).toBe("101"); + expect(submission.transactionHash).toBe("tx-hash-lockup-router"); + expect(submission.status).toBe("pending"); + }); }); + diff --git a/src/stellar/stream-nft-client.ts b/src/stellar/stream-nft-client.ts index 8ae6cb6..8e27aff 100644 --- a/src/stellar/stream-nft-client.ts +++ b/src/stellar/stream-nft-client.ts @@ -1,9 +1,9 @@ import { STREAM_KINDS, - toFundableError, type StreamNftRecord, type TransferStreamNftInput, } from "../core/index.js"; +import { toFundableError } from "./error-parser.js"; import { Client as GeneratedStreamNftClient, StreamType as GeneratedStreamType, @@ -73,10 +73,8 @@ export class StellarStreamNftClient { async isTransferable(tokenId: string | bigint): Promise { try { - const transaction = await this.client.is_transferable({ - token_id: toTokenId(tokenId), - }); - return transaction.result; + await this.ownerOf(tokenId); + return true; } catch (error) { throw toFundableError( error, @@ -92,9 +90,13 @@ export class StellarStreamNftClient { ): Promise> { assertStellarAddress(input.from, "Current NFT owner"); assertStellarAddress(input.to, "New NFT owner"); - return this.client.transfer( - { from: input.from, to: input.to, token_id: toTokenId(input.tokenId) }, - options, - ); + try { + return await this.client.transfer( + { from: input.from, to: input.to, token_id: toTokenId(input.tokenId) }, + options, + ); + } catch (error) { + throw toFundableError(error, "Failed to transfer stream NFT."); + } } } diff --git a/src/stellar/validation.ts b/src/stellar/validation.ts index 2efcca7..37aaf39 100644 --- a/src/stellar/validation.ts +++ b/src/stellar/validation.ts @@ -84,3 +84,58 @@ export function assertTokenDecimals(value: number, label = "Token decimals"): vo }); } } + +export const I128_MAX = (1n << 127n) - 1n; + +export function assertLockupUnlockAmounts( + startUnlockAmount: bigint, + cliffUnlockAmount: bigint, + totalAmount: bigint, +): void { + if (startUnlockAmount < 0n) { + throw new FundableError({ + code: FUNDABLE_ERROR_CODES.INVALID_ARGUMENT, + message: "Start unlock amount cannot be negative.", + chain: "stellar", + }); + } + if (cliffUnlockAmount < 0n) { + throw new FundableError({ + code: FUNDABLE_ERROR_CODES.INVALID_ARGUMENT, + message: "Cliff unlock amount cannot be negative.", + chain: "stellar", + }); + } + + const unlockSum = startUnlockAmount + cliffUnlockAmount; + if (unlockSum > I128_MAX) { + throw new FundableError({ + code: FUNDABLE_ERROR_CODES.INVALID_ARGUMENT, + message: "Unlock amounts sum overflowed maximum supported integer capacity.", + chain: "stellar", + }); + } + + if (unlockSum > totalAmount) { + throw new FundableError({ + code: FUNDABLE_ERROR_CODES.INVALID_ARGUMENT, + message: `Unlock amounts sum (${unlockSum.toString()}) cannot exceed total stream amount (${totalAmount.toString()}).`, + chain: "stellar", + }); + } +} + +export function assertTokenDecimalsMatch( + suppliedDecimals: number, + contractDecimals: number, + tokenLabel = "Token", +): void { + if (suppliedDecimals !== contractDecimals) { + throw new FundableError({ + code: FUNDABLE_ERROR_CODES.INVALID_ARGUMENT, + message: `Token decimals mismatch: caller supplied ${suppliedDecimals} decimals, but ${tokenLabel} contract specifies ${contractDecimals} decimals.`, + chain: "stellar", + }); + } +} +