Skip to content

Conversation

@chadoh
Copy link
Contributor

@chadoh chadoh commented May 16, 2025

We were generating a separate const Errors definition for every #[contracterror] in the original contract source. For example, with the new custom_type source code, the generated TS would be:

export const Errors = {
  /**
   * Please provide an odd number
   */
  1: {message:"NumberMustBeOdd"}
}
export const Errors = {
  /**
   * Some contract libraries contain extra #[contracterror] definitions that end up compiled
   * into the main contract types. We need to make sure tooling deals with this properly.
   */
  100: {message:"HowCouldYou"}
}

This now names the const after the name of the error enum in the original file, following the logic of other enum types. It will also rename Error to Errors, to avoid conflicts with JS Error type. So the updated custom_type source code now generates:

export const Errors = {
  /**
   * Please provide an odd number
   */
  1: {message:"NumberMustBeOdd"}
}
export const Erroneous = {
  /**
   * Some contract libraries contain extra #[contracterror] definitions that end up compiled
   * into the main contract types. We need to make sure tooling deals with this properly.
   */
  100: {message:"HowCouldYou"}
}

These constants are NOT ACTUALLY USED by the TS, but are continuing to be exported to avoid considering this a breaking change to the library generation logic.

This is needed in order to generate TS bindings for OpenZeppelin's Fungible Token. The contract itself defines contracterror number 1, the pausable libraries defines 100-series contracterrors, and the fungible library creates 200-series contracterrors.

@github-project-automation github-project-automation bot moved this from Backlog (Not Ready) to Needs Review in DevX May 16, 2025
@chadoh chadoh force-pushed the fix/ts-bindings/multiple-contracterrors branch from 235a4d1 to fae2e17 Compare May 16, 2025 18:29
@willemneal
Copy link
Contributor

I first reviewed blind to the context. Does this mean that when you have multiple error defs they are assigned a value as they are seen. e.g. first error enum with four [0-3], does this second start at value 4?

@chadoh chadoh force-pushed the fix/ts-bindings/multiple-contracterrors branch from fae2e17 to 4749a4b Compare May 16, 2025 18:33
@chadoh
Copy link
Contributor Author

chadoh commented May 16, 2025

@willemneal no, it doesn't reassign error numbers. I just checked; if you have two separate #[contracterror] definitions with re-used error numbers, you get the following invalid const Errors in the TS:

export const Errors = {
  100: {message:"Unauthorized"},
  /**
   * The operation failed because the contract is paused.
   */
  100: {message:"EnforcedPause"},
  /**
   * The operation failed because the contract is not paused.
   */
  101: {message:"ExpectedPause"}
}

Great catch. Not sure what to about this.

@chadoh
Copy link
Contributor Author

chadoh commented May 16, 2025

@willemneal ok, I dug into it, and I believe these Errors constants are never used in the current code. The const is never referenced in the generated file. Our parseError logic in AssembledTransaction in the JS SDK relies on an errorTypes map that gets passed in, and this errorTypes matches the format of Errors. But we don't need to pass in Errors (anymore?) because the error types are parsed from the contract spec when you instantiate a contract.Client. Relevant lines:

errorTypes: spec.errorCases().reduce(
  (acc, curr) => ({
    ...acc,
    [curr.value()]: { message: curr.doc().toString() },
  }),
  {} as Pick<ClientOptions, "errorTypes">,
),

I've added a second commit to remove all the logic to generate errors.

@janewang janewang requested review from fnando and leighmcculloch May 19, 2025 13:36
We were generating a separate `const Errors` definition for every
`#[contracterror]` in the original contract source. For example, with
the new `custom_type` source code, the generated TS would be:

```ts
export const Errors = {
  /**
   * Please provide an odd number
   */
  1: {message:"NumberMustBeOdd"}
}
export const Errors = {
  /**
   * Some contract libraries contain extra #[contracterror] definitions that end up compiled
   * into the main contract types. We need to make sure tooling deals with this properly.
   */
  100: {message:"HowCouldYou"}
}
```

This now names the `const` after the name of the error enum in the
original file, following the logic of other enum types. It will also
rename `Error` to `Errors`, to avoid conflicts with JS `Error` type. So
the updated `custom_type` source code now generates:

```ts
export const Errors = {
  /**
   * Please provide an odd number
   */
  1: {message:"NumberMustBeOdd"}
}
export const Erroneous = {
  /**
   * Some contract libraries contain extra #[contracterror] definitions that end up compiled
   * into the main contract types. We need to make sure tooling deals with this properly.
   */
  100: {message:"HowCouldYou"}
}
```

These constants are NOT ACTUALLY USED by the TS, but are continuing to
be exported to avoid considering this a breaking change to the library
generation logic.

This is needed in order to generate TS bindings for [OpenZeppelin's
Fungible Token][1]. The contract itself defines [`contracterror` number
1][2], the `pausable` libraries defines [100-series
`contracterror`s][3], and the `fungible` library creates [200-series
`contracterror`s][4].

  [1]: https://github.com/OpenZeppelin/stellar-contracts/blob/main/examples/fungible-token-interface/src/contract.rs
  [2]: https://github.com/OpenZeppelin/stellar-contracts/blob/54eec16f8f8d8373936147464d6eb81eff3c58c2/examples/fungible-token-interface/src/contract.rs#L29C1-L34C2
  [3]: https://github.com/OpenZeppelin/stellar-contracts/blob/54eec16f8f8d8373936147464d6eb81eff3c58c2/packages/contract-utils/pausable/src/pausable.rs#L74-L82
  [4]: https://github.com/OpenZeppelin/stellar-contracts/blob/54eec16f8f8d8373936147464d6eb81eff3c58c2/packages/tokens/fungible/src/fungible.rs#L156-L182

Co-authored-by: Willem Wyndham <willem@ahalabs.dev>
@chadoh chadoh force-pushed the fix/ts-bindings/multiple-contracterrors branch from 4a25b39 to 368af40 Compare May 22, 2025 20:02
Copy link
Member

@leighmcculloch leighmcculloch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question, but regardless lgtm.

Comment on lines +288 to +289
let name = if name == "Error" {
format!("{name}s")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know why the s is added? I see you're just copying the behaviour from above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect the original guess was that Error is a reserved word in JS. It’s not; we can name the export Error and compile with no problem. It does make for unexpected name collisions though, with the built in Error.

I figured matching the logic above would make for a more consistent file as well as less of a change from the previous behavior.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@leighmcculloch leighmcculloch enabled auto-merge (squash) May 22, 2025 21:01
@leighmcculloch leighmcculloch merged commit 0931888 into stellar:main May 22, 2025
34 checks passed
@github-project-automation github-project-automation bot moved this from Needs Review to Done in DevX May 22, 2025
@chadoh chadoh deleted the fix/ts-bindings/multiple-contracterrors branch May 22, 2025 21:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants