diff --git a/contracts/tholos/src/lib.rs b/contracts/tholos/src/lib.rs index f95fc58..7844d73 100644 --- a/contracts/tholos/src/lib.rs +++ b/contracts/tholos/src/lib.rs @@ -50,6 +50,11 @@ pub struct PauseUpdated { pub paused: bool, } +#[contractevent] +pub struct BondAmountUpdated { + pub bond_amount: i128, +} + #[contractevent] pub struct RotationProposed { pub old_resolver: Address, @@ -103,6 +108,12 @@ pub struct Assertion { /// the assertion is still pending or disputed. pub final_outcome: Option, pub outcome: bool, + /// The bond amount required to dispute this assertion and the amount + /// paid out to the winning side. Pinned to the live `DataKey::BondAmount` + /// at the moment `assert_outcome` created this assertion; a later + /// `set_bond_amount` call never changes it retroactively. Every payout + /// path (`dispute`, `finalize`, `resolve`) reads this field, never the + /// live `DataKey::BondAmount`, so this guarantee holds structurally. pub bond: i128, pub opened_at: u64, pub status: Status, @@ -565,6 +576,47 @@ impl Tholos { Ok(()) } + /// Updates the bond amount required for assertions created from this + /// point on. Only callable by the admin set at initialization, validated + /// against the same bounds `initialize` already enforces + /// (`new_bond_amount > 0`, `new_bond_amount <= MAX_BOND_AMOUNT`). + /// Pause-exempt, like `update_resolvers` and `set_paused`. + /// + /// This only affects assertions created after the change: `Assertion.bond` + /// pins the bond amount at the moment `assert_outcome` creates the + /// assertion, and every payout path (`dispute`, `finalize`, `resolve`) + /// reads `assertion.bond`, never the live `DataKey::BondAmount`. An + /// already-open assertion's payout is therefore unaffected by a later + /// `set_bond_amount` call. + /// + /// Fails with `NotInitialized` if called before `initialize`, or + /// `InvalidBondAmount` if `new_bond_amount` is zero, negative, or greater + /// than `MAX_BOND_AMOUNT`. + pub fn set_bond_amount(env: Env, new_bond_amount: i128) -> Result<(), Error> { + let admin: Address = env + .storage() + .instance() + .get(&DataKey::Admin) + .ok_or(Error::NotInitialized)?; + admin.require_auth(); + Self::touch_instance_ttl(&env); + + if new_bond_amount <= 0 || new_bond_amount > MAX_BOND_AMOUNT { + return Err(Error::InvalidBondAmount); + } + + env.storage() + .instance() + .set(&DataKey::BondAmount, &new_bond_amount); + + BondAmountUpdated { + bond_amount: new_bond_amount, + } + .publish(&env); + + Ok(()) + } + fn require_not_paused(env: &Env) -> Result<(), Error> { let paused: bool = env .storage() diff --git a/contracts/tholos/src/test.rs b/contracts/tholos/src/test.rs index 56dc48a..aeb0e41 100644 --- a/contracts/tholos/src/test.rs +++ b/contracts/tholos/src/test.rs @@ -672,6 +672,105 @@ fn test_resolvers_updated_mid_dispute_do_not_affect_it() { f.client.resolve(&f.resolvers.get(1).unwrap(), &id, &false); assert_eq!(f.token.balance(&disputer), 1_100); } +// --------------------------------------------------------------------------- +// set_bond_amount tests +// --------------------------------------------------------------------------- + +/// Covers both the happy path and the regression the issue calls for: a bond +/// change never retroactively affects an assertion opened before it. +#[test] +fn test_admin_can_set_bond_amount_and_it_only_affects_future_assertions() { + let f = Fixture::new(); + let asserter_a = f.funded_address(); + let caller = f.generate(); + + // Assertion A opens under the original bond (100). + let id_a = f.client.assert_outcome(&asserter_a, &true); + assert_eq!(f.client.get_assertion_state(&id_a).bond, DEFAULT_BOND); + + f.client.set_bond_amount(&200); + + // Assertion B, opened after the change, uses the new bond. + let asserter_b = f.funded_address(); // funded with 1_000, plenty for 200 + let id_b = f.client.assert_outcome(&asserter_b, &true); + assert_eq!(f.client.get_assertion_state(&id_b).bond, 200); + + // Assertion A is untouched: still pinned at 100, and its payout reflects + // that, not the live (now 200) bond amount. + assert_eq!(f.client.get_assertion_state(&id_a).bond, DEFAULT_BOND); + f.advance_past_window(); + f.client.finalize(&caller, &id_a); + assert_eq!(f.token.balance(&asserter_a), 1_000); // 900 + 100, not + 200 +} + +#[test] +fn test_set_bond_amount_requires_admin_auth() { + let env = Env::default(); + env.mock_all_auths(); + + let (token_id, resolvers) = setup(&env); + let contract_id = env.register(Tholos, ()); + let client = TholosClient::new(&env, &contract_id); + let admin = Address::generate(&env); + client.initialize( + &admin, + &token_id, + &DEFAULT_BOND, + &DEFAULT_WINDOW, + &resolvers, + &0u32, + ); + + client.set_bond_amount(&200); + + // env.auths() returns every require_auth invocation from the last call; + // the admin's must appear, proving set_bond_amount is admin-gated. + let auths = env.auths(); + let admin_was_authed = auths.iter().any(|(addr, _)| *addr == admin); + assert!( + admin_was_authed, + "admin's require_auth was not invoked during set_bond_amount" + ); +} + +#[test] +fn test_cannot_set_bond_amount_to_zero() { + let f = Fixture::new(); + let result = f.client.try_set_bond_amount(&0); + assert_eq!(result, Err(Ok(Error::InvalidBondAmount))); +} + +#[test] +fn test_cannot_set_bond_amount_negative() { + let f = Fixture::new(); + let result = f.client.try_set_bond_amount(&-1); + assert_eq!(result, Err(Ok(Error::InvalidBondAmount))); +} + +#[test] +fn test_cannot_set_bond_amount_above_max() { + let f = Fixture::new(); + let result = f.client.try_set_bond_amount(&(MAX_BOND_AMOUNT + 1)); + assert_eq!(result, Err(Ok(Error::InvalidBondAmount))); +} + +#[test] +fn test_can_set_bond_amount_exactly_at_max() { + let f = Fixture::new(); + let result = f.client.try_set_bond_amount(&MAX_BOND_AMOUNT); + assert_eq!(result, Ok(Ok(()))); +} + +#[test] +fn test_cannot_set_bond_amount_before_initialization() { + let env = Env::default(); + env.mock_all_auths(); + let contract_id = env.register(Tholos, ()); + let client = TholosClient::new(&env, &contract_id); + + let result = client.try_set_bond_amount(&DEFAULT_BOND); + assert_eq!(result, Err(Ok(Error::NotInitialized))); +} #[test] fn test_paused_blocks_assert_dispute_and_finalize() { diff --git a/docs/src/CONTRACT.md b/docs/src/CONTRACT.md index b04ab45..0bae7c7 100644 --- a/docs/src/CONTRACT.md +++ b/docs/src/CONTRACT.md @@ -31,7 +31,7 @@ State of an assertion: `Pending`, `Disputed`, or `Resolved`. | `asserter` | `Address` | Who posted the claim | | `outcome` | `bool` | The claimed outcome | | `final_outcome` | `Option` | The authoritative resolved outcome; `None` until the assertion reaches `Resolved` | -| `bond` | `i128` | Bond amount posted (in the configured token) | +| `bond` | `i128` | Bond amount posted (in the configured token), pinned at the moment `assert_outcome` created the assertion; a later `set_bond_amount` call never changes it retroactively | | `opened_at` | `u64` | Ledger timestamp the assertion was posted | | `status` | `Status` | Current state | | `disputer` | `Option
` | Who disputed it, if disputed | @@ -98,6 +98,19 @@ present), so a committee-driven rotation can never execute against a committee i wasn't built for. Day-to-day committee changes go through `propose_rotation` / `vote_rotation` instead. +### `set_bond_amount(new_bond_amount)` + +Updates the bond amount required for assertions created *after* this call. Requires +the stored admin's signature. Same bounds as `initialize`: `new_bond_amount` must be +positive and no greater than `MAX_BOND_AMOUNT`. Pause-exempt, like `update_resolvers` +and `set_paused`. Emits `BondAmountUpdated`. + +Has no effect on assertions already open: `Assertion.bond` pins the bond amount at +the moment `assert_outcome` created the assertion, and every payout path (`dispute`, +`finalize`, `resolve`) reads that field, never the live bond amount. Fails with +`InvalidBondAmount` if `new_bond_amount` is zero, negative, or above +`MAX_BOND_AMOUNT`, or `NotInitialized` if called before `initialize`. + ### `propose_rotation(resolver, old_resolver, new_resolver)` Proposes a single-slot committee rotation: remove `old_resolver` (must be a current @@ -246,6 +259,7 @@ history without polling `get_assertion_state`: | `Resolved` | `resolve`, once a majority is reached | `id`, `outcome` | | `ResolversUpdated` | `update_resolvers`, `vote_rotation` (on execution) | `resolvers` (the new committee) | | `PauseUpdated` | `set_paused` | `paused` | +| `BondAmountUpdated` | `set_bond_amount` | `bond_amount` (the new value) | | `RotationProposed` | `propose_rotation` | `old_resolver`, `new_resolver`, `proposed_by` | | `RotationExecuted` | `vote_rotation`, once a majority is reached | `old_resolver`, `new_resolver` | | `RotationCancelled` | `vote_rotation` (deadlock auto-cancel), `cancel_rotation`, `update_resolvers` (admin override) | `old_resolver`, `new_resolver` | diff --git a/packages/tholos-sdk/src/index.ts b/packages/tholos-sdk/src/index.ts index 20c580d..3c7bcb9 100644 --- a/packages/tholos-sdk/src/index.ts +++ b/packages/tholos-sdk/src/index.ts @@ -80,7 +80,15 @@ export type DataKey = {tag: "Admin", values: void} | {tag: "Token", values: void export interface Assertion { asserter: string; - bond: i128; + /** + * The bond amount required to dispute this assertion and the amount + * paid out to the winning side. Pinned to the live `DataKey::BondAmount` + * at the moment `assert_outcome` created this assertion; a later + * `set_bond_amount` call never changes it retroactively. Every payout + * path (`dispute`, `finalize`, `resolve`) reads this field, never the + * live `DataKey::BondAmount`, so this guarantee holds structurally. + */ +bond: i128; disputer: Option; /** * The authoritative outcome once the assertion is resolved. `None` while @@ -145,6 +153,7 @@ yes: Array; + export interface Client { /** * Construct and simulate a dispute 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. @@ -229,6 +238,27 @@ export interface Client { */ cancel_rotation: ({resolver}: {resolver: string}, options?: MethodOptions) => Promise>> + /** + * Construct and simulate a set_bond_amount 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. + * Updates the bond amount required for assertions created from this + * point on. Only callable by the admin set at initialization, validated + * against the same bounds `initialize` already enforces + * (`new_bond_amount > 0`, `new_bond_amount <= MAX_BOND_AMOUNT`). + * Pause-exempt, like `update_resolvers` and `set_paused`. + * + * This only affects assertions created after the change: `Assertion.bond` + * pins the bond amount at the moment `assert_outcome` creates the + * assertion, and every payout path (`dispute`, `finalize`, `resolve`) + * reads `assertion.bond`, never the live `DataKey::BondAmount`. An + * already-open assertion's payout is therefore unaffected by a later + * `set_bond_amount` call. + * + * Fails with `NotInitialized` if called before `initialize`, or + * `InvalidBondAmount` if `new_bond_amount` is zero, negative, or greater + * than `MAX_BOND_AMOUNT`. + */ + set_bond_amount: ({new_bond_amount}: {new_bond_amount: i128}, options?: MethodOptions) => Promise>> + /** * Construct and simulate a propose_rotation 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. * Proposes a single-slot committee rotation: remove `old_resolver` (must be @@ -290,7 +320,7 @@ export class Client extends ContractClient { "AAAABQAAAAAAAAAAAAAACEFzc2VydGVkAAAAAQAAAAhhc3NlcnRlZAAAAAMAAAAAAAAAAmlkAAAAAAAGAAAAAQAAAAAAAAAIYXNzZXJ0ZXIAAAATAAAAAAAAAAAAAAAHb3V0Y29tZQAAAAABAAAAAAAAAAI=", "AAAABQAAAAAAAAAAAAAACERpc3B1dGVkAAAAAQAAAAhkaXNwdXRlZAAAAAIAAAAAAAAAAmlkAAAAAAAGAAAAAQAAAAAAAAAIZGlzcHV0ZXIAAAATAAAAAAAAAAI=", "AAAABQAAAAAAAAAAAAAACFJlc29sdmVkAAAAAQAAAAhyZXNvbHZlZAAAAAIAAAAAAAAAAmlkAAAAAAAGAAAAAQAAAAAAAAAHb3V0Y29tZQAAAAABAAAAAAAAAAI=", - "AAAAAQAAAAAAAAAAAAAACUFzc2VydGlvbgAAAAAAAAwAAAAAAAAACGFzc2VydGVyAAAAEwAAAAAAAAAEYm9uZAAAAAsAAAAAAAAACGRpc3B1dGVyAAAD6AAAABMAAAByVGhlIGF1dGhvcml0YXRpdmUgb3V0Y29tZSBvbmNlIHRoZSBhc3NlcnRpb24gaXMgcmVzb2x2ZWQuIGBOb25lYCB3aGlsZQp0aGUgYXNzZXJ0aW9uIGlzIHN0aWxsIHBlbmRpbmcgb3IgZGlzcHV0ZWQuAAAAAAANZmluYWxfb3V0Y29tZQAAAAAAA+gAAAABAAABB1dobyBjYWxsZWQgYGZpbmFsaXplYC4gYE5vbmVgIHVudGlsIHRoZSBhc3NlcnRpb24gaXMgZmluYWxpemVkIHZpYQpgZmluYWxpemVgIChuZXZlciBzZXQgZm9yIGFzc2VydGlvbnMgcmVzb2x2ZWQgdmlhIGByZXNvbHZlYCkuIEFsd2F5cwpgU29tZWAgYWZ0ZXIgYGZpbmFsaXplYCBjb21wbGV0ZXMg4oCUIHRoZSBjYWxsZXIgbXVzdCBhdXRob3JpemUgdGhlIGNhbGwKdW5jb25kaXRpb25hbGx5LCBzbyB0aGlzIGlzIGFsd2F5cyBhIHZlcmlmaWVkIGFkZHJlc3MuAAAAAAlmaW5hbGl6ZXIAAAAAAAPoAAAAEwAAAAAAAAAJb3BlbmVkX2F0AAAAAAAABgAAAAAAAAAHb3V0Y29tZQAAAAABAAABIlRoZSByZXNvbHZlciBjb21taXR0ZWUgYXQgdGhlIG1vbWVudCB0aGlzIGFzc2VydGlvbiB3YXMgZGlzcHV0ZWQuCkVtcHR5IHVudGlsIGBkaXNwdXRlYCBpcyBjYWxsZWQuIFZvdGluZyBhbmQgbWFqb3JpdHkgYXJlIGFsd2F5cwpjb21wdXRlZCBhZ2FpbnN0IHRoaXMgc25hcHNob3QsIG5vdCB0aGUgbGl2ZSBjb21taXR0ZWUsIHNvIGFuCmB1cGRhdGVfcmVzb2x2ZXJzYCBjYWxsIG1pZC1kaXNwdXRlIGNhbid0IGNoYW5nZSB3aG8gZ2V0cyB0byBkZWNpZGUKYW4gYWxyZWFkeS1kaXNwdXRlZCBhc3NlcnRpb24uAAAAAAAJcmVzb2x2ZXJzAAAAAAAD6gAAABMAAAAAAAAABnN0YXR1cwAAAAAH0AAAAAZTdGF0dXMAAAAAAAAAAAAFdm90ZWQAAAAAAAPqAAAAEwAAAAAAAAAVdm90ZXNfYWdhaW5zdF9vdXRjb21lAAAAAAAABAAAAAAAAAARdm90ZXNfZm9yX291dGNvbWUAAAAAAAAE", + "AAAAAQAAAAAAAAAAAAAACUFzc2VydGlvbgAAAAAAAAwAAAAAAAAACGFzc2VydGVyAAAAEwAAAZFUaGUgYm9uZCBhbW91bnQgcmVxdWlyZWQgdG8gZGlzcHV0ZSB0aGlzIGFzc2VydGlvbiBhbmQgdGhlIGFtb3VudApwYWlkIG91dCB0byB0aGUgd2lubmluZyBzaWRlLiBQaW5uZWQgdG8gdGhlIGxpdmUgYERhdGFLZXk6OkJvbmRBbW91bnRgCmF0IHRoZSBtb21lbnQgYGFzc2VydF9vdXRjb21lYCBjcmVhdGVkIHRoaXMgYXNzZXJ0aW9uOyBhIGxhdGVyCmBzZXRfYm9uZF9hbW91bnRgIGNhbGwgbmV2ZXIgY2hhbmdlcyBpdCByZXRyb2FjdGl2ZWx5LiBFdmVyeSBwYXlvdXQKcGF0aCAoYGRpc3B1dGVgLCBgZmluYWxpemVgLCBgcmVzb2x2ZWApIHJlYWRzIHRoaXMgZmllbGQsIG5ldmVyIHRoZQpsaXZlIGBEYXRhS2V5OjpCb25kQW1vdW50YCwgc28gdGhpcyBndWFyYW50ZWUgaG9sZHMgc3RydWN0dXJhbGx5LgAAAAAAAARib25kAAAACwAAAAAAAAAIZGlzcHV0ZXIAAAPoAAAAEwAAAHJUaGUgYXV0aG9yaXRhdGl2ZSBvdXRjb21lIG9uY2UgdGhlIGFzc2VydGlvbiBpcyByZXNvbHZlZC4gYE5vbmVgIHdoaWxlCnRoZSBhc3NlcnRpb24gaXMgc3RpbGwgcGVuZGluZyBvciBkaXNwdXRlZC4AAAAAAA1maW5hbF9vdXRjb21lAAAAAAAD6AAAAAEAAAEHV2hvIGNhbGxlZCBgZmluYWxpemVgLiBgTm9uZWAgdW50aWwgdGhlIGFzc2VydGlvbiBpcyBmaW5hbGl6ZWQgdmlhCmBmaW5hbGl6ZWAgKG5ldmVyIHNldCBmb3IgYXNzZXJ0aW9ucyByZXNvbHZlZCB2aWEgYHJlc29sdmVgKS4gQWx3YXlzCmBTb21lYCBhZnRlciBgZmluYWxpemVgIGNvbXBsZXRlcyDigJQgdGhlIGNhbGxlciBtdXN0IGF1dGhvcml6ZSB0aGUgY2FsbAp1bmNvbmRpdGlvbmFsbHksIHNvIHRoaXMgaXMgYWx3YXlzIGEgdmVyaWZpZWQgYWRkcmVzcy4AAAAACWZpbmFsaXplcgAAAAAAA+gAAAATAAAAAAAAAAlvcGVuZWRfYXQAAAAAAAAGAAAAAAAAAAdvdXRjb21lAAAAAAEAAAEiVGhlIHJlc29sdmVyIGNvbW1pdHRlZSBhdCB0aGUgbW9tZW50IHRoaXMgYXNzZXJ0aW9uIHdhcyBkaXNwdXRlZC4KRW1wdHkgdW50aWwgYGRpc3B1dGVgIGlzIGNhbGxlZC4gVm90aW5nIGFuZCBtYWpvcml0eSBhcmUgYWx3YXlzCmNvbXB1dGVkIGFnYWluc3QgdGhpcyBzbmFwc2hvdCwgbm90IHRoZSBsaXZlIGNvbW1pdHRlZSwgc28gYW4KYHVwZGF0ZV9yZXNvbHZlcnNgIGNhbGwgbWlkLWRpc3B1dGUgY2FuJ3QgY2hhbmdlIHdobyBnZXRzIHRvIGRlY2lkZQphbiBhbHJlYWR5LWRpc3B1dGVkIGFzc2VydGlvbi4AAAAAAAlyZXNvbHZlcnMAAAAAAAPqAAAAEwAAAAAAAAAGc3RhdHVzAAAAAAfQAAAABlN0YXR1cwAAAAAAAAAAAAV2b3RlZAAAAAAAA+oAAAATAAAAAAAAABV2b3Rlc19hZ2FpbnN0X291dGNvbWUAAAAAAAAEAAAAAAAAABF2b3Rlc19mb3Jfb3V0Y29tZQAAAAAAAAQ=", "AAAABQAAAAAAAAAAAAAACUZpbmFsaXplZAAAAAAAAAEAAAAJZmluYWxpemVkAAAAAAAABAAAAAAAAAACaWQAAAAAAAYAAAABAAAAAAAAAAdvdXRjb21lAAAAAAEAAAAAAAAAt1dobyBjYWxsZWQgYGZpbmFsaXplYC4gQWx3YXlzIGEgdmVyaWZpZWQgYWRkcmVzcyDigJQgYGZpbmFsaXplYCByZXF1aXJlcwp0aGUgY2FsbGVyJ3MgYXV0aCB1bmNvbmRpdGlvbmFsbHksIHNvIHRoaXMgdmFsdWUgaXMgdHJ1c3R3b3J0aHkKcmVnYXJkbGVzcyBvZiB3aGV0aGVyIGEgcmV3YXJkIHdhcyBjb25maWd1cmVkLgAAAAAJZmluYWxpemVyAAAAAAAAEwAAAAAAAABqSG93IG1hbnkgdG9rZW5zIHdlcmUgcGFpZCB0byB0aGUgZmluYWxpemVyIGFzIGEgcmV3YXJkICgwIHdoZW4KYGZpbmFsaXplX3Jld2FyZF9icHNgIHdhcyBjb25maWd1cmVkIGFzIDApLgAAAAAABnJld2FyZAAAAAAACwAAAAAAAAAC", "AAAAAAAAAXRJbml0aWFsaXplcyB0aGUgY29udHJhY3QuIGByZXNvbHZlcnNgIG11c3QgaGF2ZSBhbiBvZGQgbGVuZ3RoIHNvIGEKc2ltcGxlIG1ham9yaXR5IHZvdGUgY2FuIG5ldmVyIHRpZS4gYGZpbmFsaXplX3Jld2FyZF9icHNgIHNldHMgdGhlCmZyYWN0aW9uIG9mIHRoZSBib25kIChpbiBiYXNpcyBwb2ludHMsIDDigJMxMDAwKSBwYWlkIHRvIHdob2V2ZXIgY2FsbHMKYGZpbmFsaXplYCBhcyBhbiBpbmNlbnRpdmUgZm9yIHByb21wdCBmaW5hbGl6YXRpb247IDAgZGlzYWJsZXMgdGhlCnJld2FyZCBlbnRpcmVseSBhbmQgcHJlc2VydmVzIHRoZSBvcmlnaW5hbCBiZWhhdmlvciB3aGVyZSB0aGUgZnVsbApib25kIGlzIHJldHVybmVkIHRvIHRoZSBhc3NlcnRlci4AAAAKaW5pdGlhbGl6ZQAAAAAABgAAAAAAAAAFYWRtaW4AAAAAAAATAAAAAAAAAAV0b2tlbgAAAAAAABMAAAAAAAAAC2JvbmRfYW1vdW50AAAAAAsAAAAAAAAAFWNoYWxsZW5nZV93aW5kb3dfc2VjcwAAAAAAAAYAAAAAAAAACXJlc29sdmVycwAAAAAAA+oAAAATAAAAAAAAABNmaW5hbGl6ZV9yZXdhcmRfYnBzAAAAAAQAAAABAAAD6QAAAAIAAAAD", "AAAAAAAAAXZQYXVzZXMgb3IgdW5wYXVzZXMgbmV3IGFzc2VydGlvbnMsIGRpc3B1dGVzLCByZXNvbHZlciB2b3RlcywgYW5kCmZpbmFsaXphdGlvbi4gQSBwZW5kaW5nIGFzc2VydGlvbiBtYXkgaGF2ZSBoYWQgbm8gcmVhbCBvcHBvcnR1bml0eSB0bwpiZSBkaXNwdXRlZCBkdXJpbmcgaXRzIGNoYWxsZW5nZSB3aW5kb3cgaWYgdGhhdCB3aW5kb3cgb3ZlcmxhcHBlZCBhCnBhdXNlLCBzbyBgZmluYWxpemVgIGlzIGJsb2NrZWQgdG9vIHJhdGhlciB0aGFuIGxldHRpbmcgaXQgZmluYWxpemUKdW5jb250ZXN0ZWQ7IGl0IGJlY29tZXMgY2FsbGFibGUgYWdhaW4gb25jZSB1bnBhdXNlZC4gT25seSBjYWxsYWJsZSBieQp0aGUgYWRtaW4gc2V0IGF0IGluaXRpYWxpemF0aW9uLgAAAAAACnNldF9wYXVzZWQAAAAAAAEAAAAAAAAABnBhdXNlZAAAAAAAAQAAAAEAAAPpAAAAAgAAAAM=", @@ -298,12 +328,14 @@ export class Client extends ContractClient { "AAAAAAAAAklBIHJlc29sdmVyIHZvdGVzIG9uIHRoZSBvcGVuIHJvdGF0aW9uIHByb3Bvc2FsLiBgYXBwcm92ZWAgcmVjb3JkcyBhIHllcyBvcgpubyAoYm90aCBwcmV2ZW50IHJlLXZvdGluZykuIE9uY2UgeWVzLXZvdGVzIHJlYWNoIGEgc3RyaWN0IG1ham9yaXR5IG9mIHRoZQpsaXZlIGNvbW1pdHRlZSwgdGhlIHJvdGF0aW9uIGV4ZWN1dGVzIGltbWVkaWF0ZWx5OiBgb2xkX3Jlc29sdmVyYCBpcyBzd2FwcGVkCmZvciBgbmV3X3Jlc29sdmVyYCBpbiB0aGUgbGl2ZSBjb21taXR0ZWUsIGFuZCB0aGUgcHJvcG9zYWwgaXMgY2xlYXJlZC4KSWYgdGhlIHJlbWFpbmluZyB1bnZvdGVkIHJlc29sdmVycyBjYW4gbm8gbG9uZ2VyIHN1cHBseSBlbm91Z2ggeWVzLXZvdGVzIHRvCnJlYWNoIGEgbWFqb3JpdHksIHRoZSBwcm9wb3NhbCBpcyBjYW5jZWxsZWQgYXV0b21hdGljYWxseSAoZGVhZGxvY2sgZ3VhcmQpLgpSZXR1cm5zIGBTb21lKHRydWUpYCBpZiB0aGUgcm90YXRpb24gZXhlY3V0ZWQsIGBTb21lKGZhbHNlKWAgaWYgaXQgd2FzCmF1dG8tY2FuY2VsbGVkIGFzIGRlYWQsIGFuZCBgTm9uZWAgaWYgdGhlIHByb3Bvc2FsIHJlbWFpbnMgb3Blbi4AAAAAAAANdm90ZV9yb3RhdGlvbgAAAAAAAAIAAAAAAAAACHJlc29sdmVyAAAAEwAAAAAAAAAHYXBwcm92ZQAAAAABAAAAAQAAA+kAAAPoAAAAAQAAAAM=", "AAAAAAAAAERQb3N0cyBhIGJvbmRlZCBjbGFpbSBhYm91dCBhbiBvdXRjb21lLiBSZXR1cm5zIHRoZSBuZXcgYXNzZXJ0aW9uIGlkLgAAAA5hc3NlcnRfb3V0Y29tZQAAAAAAAgAAAAAAAAAIYXNzZXJ0ZXIAAAATAAAAAAAAAAdvdXRjb21lAAAAAAEAAAABAAAD6QAAAAYAAAAD", "AAAAAAAAAQRDYW5jZWxzIHRoZSBvcGVuIHJvdGF0aW9uIHByb3Bvc2FsLiBUaGUgcHJvcG9zZXIgbWF5IGNhbmNlbCBhdCBhbnkgdGltZS4KQW55IGN1cnJlbnQgcmVzb2x2ZXIgbWF5IGFsc28gY2FuY2VsIG9uY2UgdGhlIHByb3Bvc2FsIGNhbiBubyBsb25nZXIgcmVhY2gKYSBtYWpvcml0eSAoZGVhZGxvY2sgZ3VhcmQpLCBzbyBhIGxvc3QgcHJvcG9zZXIga2V5IGNhbid0IHBlcm1hbmVudGx5CmJsb2NrIHJvdGF0aW9uLiBFbWl0cyBgUm90YXRpb25DYW5jZWxsZWRgLgAAAA9jYW5jZWxfcm90YXRpb24AAAAAAQAAAAAAAAAIcmVzb2x2ZXIAAAATAAAAAQAAA+kAAAACAAAAAw==", + "AAAAAAAAAztVcGRhdGVzIHRoZSBib25kIGFtb3VudCByZXF1aXJlZCBmb3IgYXNzZXJ0aW9ucyBjcmVhdGVkIGZyb20gdGhpcwpwb2ludCBvbi4gT25seSBjYWxsYWJsZSBieSB0aGUgYWRtaW4gc2V0IGF0IGluaXRpYWxpemF0aW9uLCB2YWxpZGF0ZWQKYWdhaW5zdCB0aGUgc2FtZSBib3VuZHMgYGluaXRpYWxpemVgIGFscmVhZHkgZW5mb3JjZXMKKGBuZXdfYm9uZF9hbW91bnQgPiAwYCwgYG5ld19ib25kX2Ftb3VudCA8PSBNQVhfQk9ORF9BTU9VTlRgKS4KUGF1c2UtZXhlbXB0LCBsaWtlIGB1cGRhdGVfcmVzb2x2ZXJzYCBhbmQgYHNldF9wYXVzZWRgLgoKVGhpcyBvbmx5IGFmZmVjdHMgYXNzZXJ0aW9ucyBjcmVhdGVkIGFmdGVyIHRoZSBjaGFuZ2U6IGBBc3NlcnRpb24uYm9uZGAKcGlucyB0aGUgYm9uZCBhbW91bnQgYXQgdGhlIG1vbWVudCBgYXNzZXJ0X291dGNvbWVgIGNyZWF0ZXMgdGhlCmFzc2VydGlvbiwgYW5kIGV2ZXJ5IHBheW91dCBwYXRoIChgZGlzcHV0ZWAsIGBmaW5hbGl6ZWAsIGByZXNvbHZlYCkKcmVhZHMgYGFzc2VydGlvbi5ib25kYCwgbmV2ZXIgdGhlIGxpdmUgYERhdGFLZXk6OkJvbmRBbW91bnRgLiBBbgphbHJlYWR5LW9wZW4gYXNzZXJ0aW9uJ3MgcGF5b3V0IGlzIHRoZXJlZm9yZSB1bmFmZmVjdGVkIGJ5IGEgbGF0ZXIKYHNldF9ib25kX2Ftb3VudGAgY2FsbC4KCkZhaWxzIHdpdGggYE5vdEluaXRpYWxpemVkYCBpZiBjYWxsZWQgYmVmb3JlIGBpbml0aWFsaXplYCwgb3IKYEludmFsaWRCb25kQW1vdW50YCBpZiBgbmV3X2JvbmRfYW1vdW50YCBpcyB6ZXJvLCBuZWdhdGl2ZSwgb3IgZ3JlYXRlcgp0aGFuIGBNQVhfQk9ORF9BTU9VTlRgLgAAAAAPc2V0X2JvbmRfYW1vdW50AAAAAAEAAAAAAAAAD25ld19ib25kX2Ftb3VudAAAAAALAAAAAQAAA+kAAAACAAAAAw==", "AAAAAQAAANZBbiBpbi1mbGlnaHQgc2luZ2xlLXNsb3QgY29tbWl0dGVlIHJvdGF0aW9uIHByb3Bvc2VkIGJ5IGEgY3VycmVudCByZXNvbHZlci4KRGVjaWRlZCBieSBhIHN0cmljdCBtYWpvcml0eSBvZiB0aGUgbGl2ZSBjb21taXR0ZWUgdmlhIGB2b3RlX3JvdGF0aW9uYC4gT25seQpvbmUgbWF5IGJlIG9wZW4gYXQgYSB0aW1lLiBTZWUgYGRvY3Mvc3JjL1JPVEFUSU9OX0RFU0lHTi5tZGAuAAAAAAAAAAAAEFJvdGF0aW9uUHJvcG9zYWwAAAAFAAAAPlRoZSBuZXcgcmVzb2x2ZXIgdG8gYWRkLiBNdXN0IG5vdCBhbHJlYWR5IGJlIG9uIHRoZSBjb21taXR0ZWUuAAAAAAAMbmV3X3Jlc29sdmVyAAAAEwAAAEVSZXNvbHZlcnMgd2hvIHZvdGVkIG5vLCB0byBwcmV2ZW50IGRvdWJsZS12b3RpbmcgYW5kIGRldGVjdCBkZWFkbG9jay4AAAAAAAACbm8AAAAAA+oAAAATAAAAR1RoZSBjdXJyZW50IHJlc29sdmVyIHRvIHJlbW92ZS4gTXVzdCBiZSBvbiB0aGUgY29tbWl0dGVlIHdoZW4gcHJvcG9zZWQuAAAAAAxvbGRfcmVzb2x2ZXIAAAATAAAAJVRoZSByZXNvbHZlciB3aG8gb3BlbmVkIHRoZSBwcm9wb3NhbC4AAAAAAAALcHJvcG9zZWRfYnkAAAAAEwAAADJSZXNvbHZlcnMgd2hvIHZvdGVkIHllcywgdG8gcHJldmVudCBkb3VibGUtdm90aW5nLgAAAAAAA3llcwAAAAPqAAAAEw==", "AAAAAAAAAlNQcm9wb3NlcyBhIHNpbmdsZS1zbG90IGNvbW1pdHRlZSByb3RhdGlvbjogcmVtb3ZlIGBvbGRfcmVzb2x2ZXJgIChtdXN0IGJlCmEgY3VycmVudCByZXNvbHZlcikgYW5kIGFkZCBgbmV3X3Jlc29sdmVyYCAobXVzdCBub3QgYWxyZWFkeSBiZSBvbmUpLiBPbmx5CmEgY3VycmVudCByZXNvbHZlciBtYXkgcHJvcG9zZSwgYW5kIG9ubHkgb25lIHJvdGF0aW9uIG1heSBiZSBvcGVuIGF0IGEKdGltZS4gVGhlIHByb3Bvc2FsIGlzIGRlY2lkZWQgYnkgYSBzdHJpY3QgbWFqb3JpdHkgb2YgdGhlIGxpdmUgY29tbWl0dGVlCih0aGUgc2FtZSB0aHJlc2hvbGQgdXNlZCB0byByZXNvbHZlIGRpc3B1dGVzKSB2aWEgYHZvdGVfcm90YXRpb25gLiBUaGUKY29tbWl0dGVlIHdyaXR0ZW4gb24gZXhlY3V0aW9uIGlzIHRoZSBzYW1lIGBSZXNvbHZlcnNgIHNsb3QgYHVwZGF0ZV9yZXNvbHZlcnNgCndyaXRlcywgc28gYSByb3RhdGlvbiBoYXMgbm8gZWZmZWN0IG9uIGRpc3B1dGVzIGFscmVhZHkgb3BlbiAodGhlaXIKY29tbWl0dGVlIHdhcyBzbmFwc2hvdHRlZCBhdCBgZGlzcHV0ZWAgdGltZSkuIFBhdXNlLWV4ZW1wdCwgbGlrZQpgdXBkYXRlX3Jlc29sdmVyc2AuAAAAABBwcm9wb3NlX3JvdGF0aW9uAAAAAwAAAAAAAAAIcmVzb2x2ZXIAAAATAAAAAAAAAAxvbGRfcmVzb2x2ZXIAAAATAAAAAAAAAAxuZXdfcmVzb2x2ZXIAAAATAAAAAQAAA+kAAAACAAAAAw==", "AAAAAAAAAlZSZXBsYWNlcyB0aGUgcmVzb2x2ZXIgY29tbWl0dGVlLiBPbmx5IGNhbGxhYmxlIGJ5IHRoZSBhZG1pbiBzZXQgYXQKaW5pdGlhbGl6YXRpb24uIGBuZXdfcmVzb2x2ZXJzYCBtdXN0IGhhdmUgYW4gb2RkIGxlbmd0aCBzbyBhIHNpbXBsZQptYWpvcml0eSB2b3RlIGNhbiBuZXZlciB0aWUuIENhbGxhYmxlIGV2ZW4gd2hpbGUgcGF1c2VkLCBzbyBhCmNvbXByb21pc2VkIGNvbW1pdHRlZSBjYW4gYmUgcmVwbGFjZWQgd2l0aG91dCB3YWl0aW5nIHRvIHVucGF1c2UuCgpUaGlzIGlzIHRoZSBlbWVyZ2VuY3kgb3ZlcnJpZGUgcGF0aC4gSXQgc3VwZXJzZWRlcyBhbnkgaW4tZmxpZ2h0CnNlbGYtcm90YXRpb24gdm90ZTogYW4gb3BlbiBgUm90YXRpb25Qcm9wb3NhbGAgaXMgY2xlYXJlZCAoZW1pdHRpbmcKYFJvdGF0aW9uQ2FuY2VsbGVkYCB3aGVuIG9uZSB3YXMgcHJlc2VudCksIHNvIGEgcHJvcG9zYWwgY2FuIG5ldmVyCmV4ZWN1dGUgYWdhaW5zdCBhIGNvbW1pdHRlZSBpdCB3YXNuJ3QgYnVpbHQgZm9yLiBEYXktdG8tZGF5IGNvbW1pdHRlZQpjaGFuZ2VzIGdvIHRocm91Z2ggYHByb3Bvc2Vfcm90YXRpb25gIC8gYHZvdGVfcm90YXRpb25gIGluc3RlYWQuAAAAAAAQdXBkYXRlX3Jlc29sdmVycwAAAAEAAAAAAAAADW5ld19yZXNvbHZlcnMAAAAAAAPqAAAAEwAAAAEAAAPpAAAAAgAAAAM=", "AAAABQAAAAAAAAAAAAAAEFJlc29sdmVyc1VwZGF0ZWQAAAABAAAAEXJlc29sdmVyc191cGRhdGVkAAAAAAAAAQAAAAAAAAAJcmVzb2x2ZXJzAAAAAAAD6gAAABMAAAAAAAAAAg==", "AAAABQAAAAAAAAAAAAAAEFJvdGF0aW9uRXhlY3V0ZWQAAAABAAAAEXJvdGF0aW9uX2V4ZWN1dGVkAAAAAAAAAgAAAAAAAAAMb2xkX3Jlc29sdmVyAAAAEwAAAAAAAAAAAAAADG5ld19yZXNvbHZlcgAAABMAAAAAAAAAAg==", "AAAABQAAAAAAAAAAAAAAEFJvdGF0aW9uUHJvcG9zZWQAAAABAAAAEXJvdGF0aW9uX3Byb3Bvc2VkAAAAAAAAAwAAAAAAAAAMb2xkX3Jlc29sdmVyAAAAEwAAAAAAAAAAAAAADG5ld19yZXNvbHZlcgAAABMAAAAAAAAAAAAAAAtwcm9wb3NlZF9ieQAAAAATAAAAAAAAAAI=", + "AAAABQAAAAAAAAAAAAAAEUJvbmRBbW91bnRVcGRhdGVkAAAAAAAAAQAAABNib25kX2Ftb3VudF91cGRhdGVkAAAAAAEAAAAAAAAAC2JvbmRfYW1vdW50AAAAAAsAAAAAAAAAAg==", "AAAABQAAAAAAAAAAAAAAEVJvdGF0aW9uQ2FuY2VsbGVkAAAAAAAAAQAAABJyb3RhdGlvbl9jYW5jZWxsZWQAAAAAAAIAAAAAAAAADG9sZF9yZXNvbHZlcgAAABMAAAAAAAAAAAAAAAxuZXdfcmVzb2x2ZXIAAAATAAAAAAAAAAI=", "AAAAAAAAAAAAAAATZ2V0X2Fzc2VydGlvbl9zdGF0ZQAAAAABAAAAAAAAAAJpZAAAAAAABgAAAAEAAAPpAAAH0AAAAAlBc3NlcnRpb24AAAAAAAAD" ]), options @@ -318,6 +350,7 @@ export class Client extends ContractClient { vote_rotation: this.txFromJSON>>, assert_outcome: this.txFromJSON>, cancel_rotation: this.txFromJSON>, + set_bond_amount: this.txFromJSON>, propose_rotation: this.txFromJSON>, update_resolvers: this.txFromJSON>, get_assertion_state: this.txFromJSON>