Переглянути джерело

lang: add additional `require_x` comparison macros (#1622)

Matthew Callens 3 роки тому
батько
коміт
b376fd4615

+ 3 - 2
CHANGELOG.md

@@ -15,12 +15,13 @@ incremented for features.
 
 * cli: Add `anchor clean` command that's the same as `cargo clean` but preserves keypairs inside `target/deploy` ([#1470](https://github.com/project-serum/anchor/issues/1470)).
 * cli: Running `anchor init` now initializes a new git repository for the workspace. This can be disabled with the `--no-git` flag ([#1605](https://github.com/project-serum/anchor/pull/1605)).
+* cli: Add support for `anchor idl fetch` to work outside anchor workspace ([#1509](https://github.com/project-serum/anchor/pull/1509)).
+* cli: [[test.validator.clone]] also clones the program data account of programs owned by the bpf upgradeable loader ([#1481](https://github.com/project-serum/anchor/issues/1481)).
 * lang: Add new `AccountSysvarMismatch` error code and test cases for sysvars ([#1535](https://github.com/project-serum/anchor/pull/1535)).
 * lang: Replace `std::io::Cursor` with a custom `Write` impl that uses the Solana mem syscalls ([#1589](https://github.com/project-serum/anchor/pull/1589)).
+* lang: Add `require_neq`, `require_keys_neq`, `require_gt`, and `require_gte` comparison macros ([#1622](https://github.com/project-serum/anchor/pull/1622)).
 * spl: Add support for revoke instruction ([#1493](https://github.com/project-serum/anchor/pull/1493)).
-* cli: Add support for `anchor idl fetch` to work outside anchor workspace ([#1509](https://github.com/project-serum/anchor/pull/1509)).
 * ts: Add provider parameter to `Spl.token` factory method ([#1597](https://github.com/project-serum/anchor/pull/1597)).
-* cli: [[test.validator.clone]] also clones the program data account of programs owned by the bpf upgradeable loader ([#1481](https://github.com/project-serum/anchor/issues/1481)).
 
 ### Fixes
 

+ 12 - 0
lang/src/error.rs

@@ -116,6 +116,18 @@ pub enum ErrorCode {
     /// 2502 - A require_keys_eq expression was violated
     #[msg("A require_keys_eq expression was violated")]
     RequireKeysEqViolated,
+    /// 2503 - A require_neq expression was violated
+    #[msg("A require_neq expression was violated")]
+    RequireNeqViolated,
+    /// 2504 - A require_keys_neq expression was violated
+    #[msg("A require_keys_neq expression was violated")]
+    RequireKeysNeqViolated,
+    /// 2505 - A require_gt expression was violated
+    #[msg("A require_gt expression was violated")]
+    RequireGtViolated,
+    /// 2506 - A require_gte expression was violated
+    #[msg("A require_gte expression was violated")]
+    RequireGteViolated,
 
     // Accounts.
     /// 3000 - The account discriminator was already set on this account

+ 123 - 3
lang/src/lib.rs

@@ -239,9 +239,9 @@ pub mod prelude {
         accounts::signer::Signer, accounts::system_account::SystemAccount,
         accounts::sysvar::Sysvar, accounts::unchecked_account::UncheckedAccount, constant,
         context::Context, context::CpiContext, declare_id, emit, err, error, event, interface,
-        program, require, require_eq, require_keys_eq,
-        solana_program::bpf_loader_upgradeable::UpgradeableLoaderState, source, state, zero_copy,
-        AccountDeserialize, AccountSerialize, Accounts, AccountsExit, AnchorDeserialize,
+        program, require, require_eq, require_gt, require_gte, require_keys_eq, require_keys_neq,
+        require_neq, solana_program::bpf_loader_upgradeable::UpgradeableLoaderState, source, state,
+        zero_copy, AccountDeserialize, AccountSerialize, Accounts, AccountsExit, AnchorDeserialize,
         AnchorSerialize, Id, Key, Owner, ProgramData, Result, System, ToAccountInfo,
         ToAccountInfos, ToAccountMetas,
     };
@@ -397,6 +397,36 @@ macro_rules! require_eq {
     };
 }
 
+/// Ensures two NON-PUBKEY values are not equal.
+///
+/// Use [require_keys_neq](crate::prelude::require_keys_neq)
+/// to compare two pubkeys.
+///
+/// Can be used with or without a custom error code.
+///
+/// # Example
+/// ```rust,ignore
+/// pub fn set_data(ctx: Context<SetData>, data: u64) -> Result<()> {
+///     require_neq!(ctx.accounts.data.data, 0);
+///     ctx.accounts.data.data = data;
+///     Ok(());
+/// }
+/// ```
+#[macro_export]
+macro_rules! require_neq {
+    ($value1: expr, $value2: expr, $error_code: expr $(,)?) => {
+        if $value1 == $value2 {
+            return Err(error!($error_code).with_values(($value1, $value2)));
+        }
+    };
+    ($value1: expr, $value2: expr $(,)?) => {
+        if $value1 == $value2 {
+            return Err(error!(anchor_lang::error::ErrorCode::RequireNeqViolated)
+                .with_values(($value1, $value2)));
+        }
+    };
+}
+
 /// Ensures two pubkeys values are equal.
 ///
 /// Use [require_eq](crate::prelude::require_eq)
@@ -427,6 +457,96 @@ macro_rules! require_keys_eq {
     };
 }
 
+/// Ensures two pubkeys are not equal.
+///
+/// Use [require_neq](crate::prelude::require_neq)
+/// to compare two non-pubkey values.
+///
+/// Can be used with or without a custom error code.
+///
+/// # Example
+/// ```rust,ignore
+/// pub fn set_data(ctx: Context<SetData>, data: u64) -> Result<()> {
+///     require_keys_neq!(ctx.accounts.data.authority.key(), ctx.accounts.other.key());
+///     ctx.accounts.data.data = data;
+///     Ok(())
+/// }
+/// ```
+#[macro_export]
+macro_rules! require_keys_neq {
+    ($value1: expr, $value2: expr, $error_code: expr $(,)?) => {
+        if $value1 == $value2 {
+            return Err(error!($error_code).with_pubkeys(($value1, $value2)));
+        }
+    };
+    ($value1: expr, $value2: expr $(,)?) => {
+        if $value1 == $value2 {
+            return Err(
+                error!(anchor_lang::error::ErrorCode::RequireKeysNeqViolated)
+                    .with_pubkeys(($value1, $value2)),
+            );
+        }
+    };
+}
+
+/// Ensures the first NON-PUBKEY value is greater than the second
+/// NON-PUBKEY value.
+///
+/// To include an equality check, use [require_gte](crate::require_gte).
+///
+/// Can be used with or without a custom error code.
+///
+/// # Example
+/// ```rust,ignore
+/// pub fn set_data(ctx: Context<SetData>, data: u64) -> Result<()> {
+///     require_gt!(ctx.accounts.data.data, 0);
+///     ctx.accounts.data.data = data;
+///     Ok(());
+/// }
+/// ```
+#[macro_export]
+macro_rules! require_gt {
+    ($value1: expr, $value2: expr, $error_code: expr $(,)?) => {
+        if $value1 <= $value2 {
+            return Err(error!($error_code).with_values(($value1, $value2)));
+        }
+    };
+    ($value1: expr, $value2: expr $(,)?) => {
+        if $value1 <= $value2 {
+            return Err(error!(anchor_lang::error::ErrorCode::RequireGtViolated)
+                .with_values(($value1, $value2)));
+        }
+    };
+}
+
+/// Ensures the first NON-PUBKEY value is greater than or equal
+/// to the second NON-PUBKEY value.
+///
+/// Can be used with or without a custom error code.
+///
+/// # Example
+/// ```rust,ignore
+/// pub fn set_data(ctx: Context<SetData>, data: u64) -> Result<()> {
+///     require_gte!(ctx.accounts.data.data, 1);
+///     ctx.accounts.data.data = data;
+///     Ok(());
+/// }
+/// ```
+#[macro_export]
+macro_rules! require_gte {
+    ($value1: expr, $value2: expr, $error_code: expr $(,)?) => {
+        if $value1 < $value2 {
+            return Err(error!($error_code).with_values(($value1, $value2)));
+        }
+    };
+    ($value1: expr, $value2: expr $(,)?) => {
+        if $value1 < $value2 {
+            return Err(error!(anchor_lang::error::ErrorCode::RequireGteViolated)
+                .with_values(($value1, $value2)));
+        }
+    };
+}
+
 /// Returns with the given error.
 /// Use this with a custom error type.
 ///

+ 1 - 0
tests/errors/Anchor.toml

@@ -9,3 +9,4 @@ errors = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
 test = "yarn run mocha -t 1000000 tests/"
 
 [features]
+seeds = false

+ 67 - 2
tests/errors/programs/errors/src/lib.rs

@@ -74,8 +74,22 @@ mod errors {
         Ok(())
     }
 
+    pub fn require_neq(_ctx: Context<RequireNeq>) -> Result<()> {
+        require_neq!(500, 500, MyError::ValueMatch);
+        Ok(())
+    }
+
+    pub fn require_neq_default_error(_ctx: Context<RequireNeq>) -> Result<()> {
+        require_neq!(500, 500);
+        Ok(())
+    }
+
     pub fn require_keys_eq(ctx: Context<RequireKeysEq>) -> Result<()> {
-        require_keys_eq!(ctx.accounts.some_account.key(), *ctx.program_id, MyError::ValueMismatch);
+        require_keys_eq!(
+            ctx.accounts.some_account.key(),
+            *ctx.program_id,
+            MyError::ValueMismatch
+        );
         Ok(())
     }
 
@@ -83,6 +97,40 @@ mod errors {
         require_keys_eq!(ctx.accounts.some_account.key(), *ctx.program_id);
         Ok(())
     }
+
+    pub fn require_keys_neq(ctx: Context<RequireKeysNeq>) -> Result<()> {
+        require_keys_neq!(
+            ctx.accounts.some_account.key(),
+            *ctx.program_id,
+            MyError::ValueMatch
+        );
+        Ok(())
+    }
+
+    pub fn require_keys_neq_default_error(ctx: Context<RequireKeysNeq>) -> Result<()> {
+        require_keys_neq!(ctx.accounts.some_account.key(), *ctx.program_id);
+        Ok(())
+    }
+
+    pub fn require_gt(_ctx: Context<RequireGt>) -> Result<()> {
+        require_gt!(5, 10, MyError::ValueLessOrEqual);
+        Ok(())
+    }
+
+    pub fn require_gt_default_error(_ctx: Context<RequireGt>) -> Result<()> {
+        require_gt!(10, 10);
+        Ok(())
+    }
+
+    pub fn require_gte(_ctx: Context<RequireGt>) -> Result<()> {
+        require_gte!(5, 10, MyError::ValueLess);
+        Ok(())
+    }
+
+    pub fn require_gte_default_error(_ctx: Context<RequireGt>) -> Result<()> {
+        require_gte!(5, 10);
+        Ok(())
+    }
 }
 
 #[derive(Accounts)]
@@ -133,9 +181,23 @@ pub struct AccountOwnedByWrongProgramError<'info> {
 #[derive(Accounts)]
 pub struct RequireEq {}
 
+#[derive(Accounts)]
+pub struct RequireNeq {}
+
+#[derive(Accounts)]
+pub struct RequireGt {}
+
+#[derive(Accounts)]
+pub struct RequireGte {}
+
 #[derive(Accounts)]
 pub struct RequireKeysEq<'info> {
-    pub some_account: UncheckedAccount<'info>
+    pub some_account: UncheckedAccount<'info>,
+}
+
+#[derive(Accounts)]
+pub struct RequireKeysNeq<'info> {
+    pub some_account: UncheckedAccount<'info>,
 }
 
 #[error_code]
@@ -146,4 +208,7 @@ pub enum MyError {
     HelloNext,
     HelloCustom,
     ValueMismatch,
+    ValueMatch,
+    ValueLess,
+    ValueLessOrEqual,
 }

+ 152 - 2
tests/errors/tests/errors.js

@@ -333,6 +333,40 @@ describe("errors", () => {
     ]);
   });
 
+  it("Emits a ValueMatch error via require_neq", async () => {
+    await withLogTest(async () => {
+      try {
+        const tx = await program.rpc.requireNeq();
+        assert.fail(
+          "Unexpected success in creating a transaction that should have failed with `ValueMatch` error"
+        );
+      } catch (err) {
+        assert.equal(err.code, 6127);
+      }
+    }, [
+      "Program log: AnchorError thrown in programs/errors/src/lib.rs:78. Error Code: ValueMatch. Error Number: 6127. Error Message: ValueMatch.",
+      "Program log: Left: 500",
+      "Program log: Right: 500",
+    ]);
+  });
+
+  it("Emits a RequireNeqViolated error via require_neq", async () => {
+    await withLogTest(async () => {
+      try {
+        const tx = await program.rpc.requireNeqDefaultError();
+        assert.fail(
+          "Unexpected success in creating a transaction that should have failed with `RequireNeqViolated` error"
+        );
+      } catch (err) {
+        assert.equal(err.code, 2503);
+      }
+    }, [
+      "Program log: AnchorError thrown in programs/errors/src/lib.rs:83. Error Code: RequireNeqViolated. Error Number: 2503. Error Message: A require_neq expression was violated.",
+      "Program log: Left: 500",
+      "Program log: Right: 500",
+    ]);
+  });
+
   it("Emits a ValueMismatch error via require_keys_eq", async () => {
     const someAccount = anchor.web3.Keypair.generate().publicKey;
     await withLogTest(async () => {
@@ -349,7 +383,7 @@ describe("errors", () => {
         assert.equal(err.code, 6126);
       }
     }, [
-      "Program log: AnchorError thrown in programs/errors/src/lib.rs:78. Error Code: ValueMismatch. Error Number: 6126. Error Message: ValueMismatch.",
+      "Program log: AnchorError thrown in programs/errors/src/lib.rs:88. Error Code: ValueMismatch. Error Number: 6126. Error Message: ValueMismatch.",
       "Program log: Left:",
       `Program log: ${someAccount}`,
       "Program log: Right:",
@@ -373,11 +407,127 @@ describe("errors", () => {
         assert.equal(err.code, 2502);
       }
     }, [
-      "Program log: AnchorError thrown in programs/errors/src/lib.rs:83. Error Code: RequireKeysEqViolated. Error Number: 2502. Error Message: A require_keys_eq expression was violated.",
+      "Program log: AnchorError thrown in programs/errors/src/lib.rs:97. Error Code: RequireKeysEqViolated. Error Number: 2502. Error Message: A require_keys_eq expression was violated.",
+      "Program log: Left:",
+      `Program log: ${someAccount}`,
+      "Program log: Right:",
+      `Program log: ${program.programId}`,
+    ]);
+  });
+
+  it("Emits a ValueMatch error via require_keys_neq", async () => {
+    const someAccount = program.programId;
+    await withLogTest(async () => {
+      try {
+        const tx = await program.rpc.requireKeysNeq({
+          accounts: {
+            someAccount,
+          },
+        });
+        assert.fail(
+          "Unexpected success in creating a transaction that should have failed with `ValueMatch` error"
+        );
+      } catch (err) {
+        assert.equal(err.code, 6127);
+      }
+    }, [
+      "Program log: AnchorError thrown in programs/errors/src/lib.rs:102. Error Code: ValueMatch. Error Number: 6127. Error Message: ValueMatch.",
+      "Program log: Left:",
+      `Program log: ${someAccount}`,
+      "Program log: Right:",
+      `Program log: ${program.programId}`,
+    ]);
+  });
+
+  it("Emits a RequireKeysNeqViolated error via require_keys_neq", async () => {
+    const someAccount = program.programId;
+    await withLogTest(async () => {
+      try {
+        const tx = await program.rpc.requireKeysNeqDefaultError({
+          accounts: {
+            someAccount,
+          },
+        });
+        assert.fail(
+          "Unexpected success in creating a transaction that should have failed with `RequireKeysNeqViolated` error"
+        );
+      } catch (err) {
+        assert.equal(err.code, 2504);
+      }
+    }, [
+      "Program log: AnchorError thrown in programs/errors/src/lib.rs:111. Error Code: RequireKeysNeqViolated. Error Number: 2504. Error Message: A require_keys_neq expression was violated.",
       "Program log: Left:",
       `Program log: ${someAccount}`,
       "Program log: Right:",
       `Program log: ${program.programId}`,
     ]);
   });
+
+  it("Emits a ValueLessOrEqual error via require_gt", async () => {
+    await withLogTest(async () => {
+      try {
+        const tx = await program.rpc.requireGt();
+        assert.fail(
+          "Unexpected success in creating a transaction that should have failed with `ValueLessOrEqual` error"
+        );
+      } catch (err) {
+        assert.equal(err.code, 6129);
+      }
+    }, [
+      "Program log: AnchorError thrown in programs/errors/src/lib.rs:116. Error Code: ValueLessOrEqual. Error Number: 6129. Error Message: ValueLessOrEqual.",
+      "Program log: Left: 5",
+      "Program log: Right: 10",
+    ]);
+  });
+
+  it("Emits a RequireGtViolated error via require_gt", async () => {
+    await withLogTest(async () => {
+      try {
+        const tx = await program.rpc.requireGtDefaultError();
+        assert.fail(
+          "Unexpected success in creating a transaction that should have failed with `RequireGtViolated` error"
+        );
+      } catch (err) {
+        assert.equal(err.code, 2505);
+      }
+    }, [
+      "Program log: AnchorError thrown in programs/errors/src/lib.rs:121. Error Code: RequireGtViolated. Error Number: 2505. Error Message: A require_gt expression was violated.",
+      "Program log: Left: 10",
+      "Program log: Right: 10",
+    ]);
+  });
+
+  it("Emits a ValueLess error via require_gte", async () => {
+    await withLogTest(async () => {
+      try {
+        const tx = await program.rpc.requireGte();
+        assert.fail(
+          "Unexpected success in creating a transaction that should have failed with `ValueLess` error"
+        );
+      } catch (err) {
+        assert.equal(err.code, 6128);
+      }
+    }, [
+      "Program log: AnchorError thrown in programs/errors/src/lib.rs:126. Error Code: ValueLess. Error Number: 6128. Error Message: ValueLess.",
+      "Program log: Left: 5",
+      "Program log: Right: 10",
+    ]);
+  });
+
+  it("Emits a RequireGteViolated error via require_gte", async () => {
+    await withLogTest(async () => {
+      try {
+        const tx = await program.rpc.requireGteDefaultError();
+        assert.fail(
+          "Unexpected success in creating a transaction that should have failed with `RequireGteViolated` error"
+        );
+      } catch (err) {
+        assert.equal(err.code, 2506);
+      }
+    }, [
+      "Program log: AnchorError thrown in programs/errors/src/lib.rs:131. Error Code: RequireGteViolated. Error Number: 2506. Error Message: A require_gte expression was violated.",
+      "Program log: Left: 5",
+      "Program log: Right: 10",
+    ]);
+  });
 });

+ 87 - 6
tests/yarn.lock

@@ -50,12 +50,12 @@
     snake-case "^3.0.4"
     toml "^3.0.0"
 
-"@project-serum/anchor@^0.21.0":
-  version "0.21.0"
-  resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.21.0.tgz#ad5fb33744991ec1900cdb2fd22707c908b12b5f"
-  integrity sha512-flRuW/F+iC8mitNokx82LOXyND7Dyk6n5UUPJpQv/+NfySFrNFlzuQZaBZJ4CG5g9s8HS/uaaIz1nVkDR8V/QA==
+"@project-serum/anchor@^0.22.1":
+  version "0.22.1"
+  resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.22.1.tgz#698a9620f94691de0a12bbc650a5c8380e2f0e8a"
+  integrity sha512-5pHeyvQhzLahIQ8aZymmDMZJAJFklN0joZdI+YIqFkK2uU/mlKr6rBLQjxysf/j1mLLiNG00tdyLfUtTAdQz7w==
   dependencies:
-    "@project-serum/borsh" "^0.2.4"
+    "@project-serum/borsh" "^0.2.5"
     "@solana/web3.js" "^1.17.0"
     base64-js "^1.5.1"
     bn.js "^5.1.2"
@@ -79,7 +79,7 @@
     bn.js "^5.1.2"
     buffer-layout "^1.2.0"
 
-"@project-serum/borsh@^0.2.4":
+"@project-serum/borsh@^0.2.5":
   version "0.2.5"
   resolved "https://registry.yarnpkg.com/@project-serum/borsh/-/borsh-0.2.5.tgz#6059287aa624ecebbfc0edd35e4c28ff987d8663"
   integrity sha512-UmeUkUoKdQ7rhx6Leve1SssMR/Ghv8qrEiyywyxSWg7ooV7StdpPBhciiy5eB3T0qU1BXvdRNC8TdrkxK7WC5Q==
@@ -421,6 +421,21 @@ chokidar@3.5.2:
   optionalDependencies:
     fsevents "~2.3.2"
 
+chokidar@3.5.3:
+  version "3.5.3"
+  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
+  integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
+  dependencies:
+    anymatch "~3.1.2"
+    braces "~3.0.2"
+    glob-parent "~5.1.2"
+    is-binary-path "~2.1.0"
+    is-glob "~4.0.1"
+    normalize-path "~3.0.0"
+    readdirp "~3.6.0"
+  optionalDependencies:
+    fsevents "~2.3.2"
+
 circular-json@^0.5.9:
   version "0.5.9"
   resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.5.9.tgz#932763ae88f4f7dead7a0d09c8a51a4743a53b1d"
@@ -483,6 +498,13 @@ debug@4.3.2:
   dependencies:
     ms "2.1.2"
 
+debug@4.3.3:
+  version "4.3.3"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
+  integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
+  dependencies:
+    ms "2.1.2"
+
 decamelize@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837"
@@ -639,6 +661,18 @@ glob@7.1.7:
     once "^1.3.0"
     path-is-absolute "^1.0.0"
 
+glob@7.2.0:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
+  integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
+  dependencies:
+    fs.realpath "^1.0.0"
+    inflight "^1.0.4"
+    inherits "2"
+    minimatch "^3.0.4"
+    once "^1.3.0"
+    path-is-absolute "^1.0.0"
+
 growl@1.10.5:
   version "1.10.5"
   resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
@@ -847,6 +881,13 @@ minimatch@3.0.4, minimatch@^3.0.4:
   dependencies:
     brace-expansion "^1.1.7"
 
+minimatch@4.2.1:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.1.tgz#40d9d511a46bdc4e563c22c3080cde9c0d8299b4"
+  integrity sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==
+  dependencies:
+    brace-expansion "^1.1.7"
+
 minimist@^1.2.0, minimist@^1.2.5:
   version "1.2.5"
   resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
@@ -889,6 +930,36 @@ mocha@^9.1.3:
     yargs-parser "20.2.4"
     yargs-unparser "2.0.0"
 
+mocha@^9.2.2:
+  version "9.2.2"
+  resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.2.2.tgz#d70db46bdb93ca57402c809333e5a84977a88fb9"
+  integrity sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==
+  dependencies:
+    "@ungap/promise-all-settled" "1.1.2"
+    ansi-colors "4.1.1"
+    browser-stdout "1.3.1"
+    chokidar "3.5.3"
+    debug "4.3.3"
+    diff "5.0.0"
+    escape-string-regexp "4.0.0"
+    find-up "5.0.0"
+    glob "7.2.0"
+    growl "1.10.5"
+    he "1.2.0"
+    js-yaml "4.1.0"
+    log-symbols "4.1.0"
+    minimatch "4.2.1"
+    ms "2.1.3"
+    nanoid "3.3.1"
+    serialize-javascript "6.0.0"
+    strip-json-comments "3.1.1"
+    supports-color "8.1.1"
+    which "2.0.2"
+    workerpool "6.2.0"
+    yargs "16.2.0"
+    yargs-parser "20.2.4"
+    yargs-unparser "2.0.0"
+
 ms@2.1.2:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
@@ -904,6 +975,11 @@ nanoid@3.1.25:
   resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.25.tgz#09ca32747c0e543f0e1814b7d3793477f9c8e152"
   integrity sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==
 
+nanoid@3.3.1:
+  version "3.3.1"
+  resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35"
+  integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==
+
 no-case@^3.0.4:
   version "3.0.4"
   resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"
@@ -1255,6 +1331,11 @@ workerpool@6.1.5:
   resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.5.tgz#0f7cf076b6215fd7e1da903ff6f22ddd1886b581"
   integrity sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==
 
+workerpool@6.2.0:
+  version "6.2.0"
+  resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.0.tgz#827d93c9ba23ee2019c3ffaff5c27fccea289e8b"
+  integrity sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==
+
 wrap-ansi@^7.0.0:
   version "7.0.0"
   resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"

+ 11 - 0
ts/src/error.ts

@@ -99,6 +99,10 @@ const LangErrorCode = {
   RequireViolated: 2500,
   RequireEqViolated: 2501,
   RequireKeysEqViolated: 2502,
+  RequireNeqViolated: 2503,
+  RequireKeysNeqViolated: 2504,
+  RequireGtViolated: 2505,
+  RequireGteViolated: 2506,
 
   // Accounts.
   AccountDiscriminatorAlreadySet: 3000,
@@ -197,6 +201,13 @@ const LangErrorMessage = new Map([
     LangErrorCode.RequireKeysEqViolated,
     "A require_keys_eq expression was violated",
   ],
+  [LangErrorCode.RequireNeqViolated, "A require_neq expression was violated"],
+  [
+    LangErrorCode.RequireKeysNeqViolated,
+    "A require_keys_neq expression was violated",
+  ],
+  [LangErrorCode.RequireGtViolated, "A require_gt expression was violated"],
+  [LangErrorCode.RequireGteViolated, "A require_gte expression was violated"],
 
   // Accounts.
   [