ChainSafe / ChainSafe/open-creator-rails.unity
Catch and Decode Custom Smart Contract Errors on Every Contract Call
- Dominant language
- C#
- Stars
- 0
- Forks
- 0
- PR merge metrics
- No merged PRs in 30d
Description
**What**
Every contract call in `Asset.cs` — both state-mutating (`SendRequestAndWaitForReceiptAsync`) and read-only (`QueryAsync`) — currently propagates a Nethereum `SmartContractCustomErrorRevertException` on a revert, exposing opaque hex-encoded error data to callers. All three generated services already register their full set of typed error DTOs via `GetAllErrorTypes()` (22 errors in `AssetService`, 7 in `AssetRegistryService`, 14 in `ERC20PermitService`), but nothing in the SDK ever uses this registry to decode a revert.
Add a typed `ContractException` (and generic `ContractException`) that is always thrown in place of the raw exception whenever a Solidity custom error can be decoded, making the error transparent and strongly-typed to every caller.
**Why**
Without decoding, callers receive no actionable information when a contract call reverts. A caller catching a `SubscriptionNotFoundError` revert currently sees only a `SmartContractCustomErrorRevertException` with a raw hex payload — they cannot distinguish it from a network error, an `OwnableUnauthorizedAccountError`, or a `PermitFailedError`. Typed exceptions make error handling correct and ergonomic.
**How**
1. **Create `ContractException.cs`** — a new exception type in `Runtime/Utils/` (or `Runtime/Exceptions/`):
- Non-generic `ContractException` base with an `IErrorDTO Error` property and human-readable message
- Generic `ContractException` where `TError : IErrorDTO` for callers who want to pattern-match the specific error type
2. **Decode using `Service.FindCustomErrorException()`** — the generated service already exposes a `FindCustomErrorException(SmartContractCustomErrorRevertException)` method that matches the revert data against all registered error types and returns a decoded result. Access the decoded error via `.DecodedError` on the return value. No separate utility class is required.
3. **Wrap every contract call in `Asset.cs`** with a try/catch on `SmartContractCustomErrorRevertException` that calls `Service.FindCustomErrorException(e).DecodedError` and rethrows as `ContractException` — for example:
```csharp
try
{
var receipt = await Service.SetSubscriptionPriceRequestAndWaitForReceiptAsync(newSubscriptionPrice);
}
catch (SmartContractCustomErrorRevertException e)
{
throw new ContractException(Service.FindCustomErrorException(e).DecodedError);
}
```
This covers:
- All `SendRequestAndWaitForReceiptAsync` calls: `Subscribe`, `CancelSubscription`, `SetSubscriptionPrice`, `ClaimCreatorFee` (single + batch), `RevokeSubscription`, `UnrevokeSubscription`
- All `QueryAsync` / `QueryDeserializingToObjectAsync` calls in `Connected()`, `GetSubscriptionExpiration`, `IsSubscriptionExpired`, `IsSubscriberRevoked`, `IsSubscriptionActive`, `GetSubscriptionPriceAndDuration`, `GetPermit`
4. **Update `AssertOwner()`** — currently throws a plain `UnauthorizedAccessException(nameof(OwnableInvalidOwnerError))`; replace with `ContractException` for consistency
5. **Add tests** covering at minimum:
- `Subscribe` reverts with `SubscriptionNotFoundError` → caught as `ContractException`
- `Subscribe` reverts with `PermitFailedError` → caught as `ContractException`
- `RevokeSubscription` reverts with `SubscriptionAlreadyRevokedError`
- A read-only query revert is also decoded correctly
**Acceptance Criteria**
- [ ] `ContractException` and `ContractException` exist in the Runtime
- [ ] Every contract call in `Asset.cs` catches `SmartContractCustomErrorRevertException` and rethrows as a `ContractException` — no raw `SmartContractCustomErrorRevertException` escapes the `Asset` boundary
- [ ] The decoded `IErrorDTO` instance (with any parameters, e.g. `OwnableUnauthorizedAccountError.Account`) is accessible on the exception
- [ ] `AssertOwner()` uses `ContractException` rather than `UnauthorizedAccessException`
- [ ] Tests pass for the key revert scenarios listed above
**Estimation**
**Dependencies**
None.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.