feat(encryption): support ERC-7730 encryption schemes - #56
Conversation
ae43f5d to
c8dda18
Compare
| }; | ||
| } | ||
|
|
||
| return { value: bytesSliceToFieldType(bytes, fieldType) }; |
There was a problem hiding this comment.
before returning this, should we validate the plaintext type vs the bytes lenght ?
for ex by adding :
const maxWidth = plaintextByteWidth(plaintextType); // ex: "uint64" → 8; bytes/string → undefined
if (maxWidth !== undefined && bytes.length > maxWidth) {
return {
warning: warn(
"DECRYPTION_FAILED",
`Decrypted '${scheme}' value is ${bytes.length} bytes, exceeds ${plaintextType}`,
),
};
}
with plaintextByteWidth a helper function that returns how many bytes a plaintext type should be at most
|
hey @manuelwedler , any updates on this ? |
c156778 to
c8dda18
Compare
Implements the `encryption` field annotation: encrypted field values are decrypted via a new wallet-provided `resolveDecryptedValue` callback, then rendered with the field's regular format. Falls back to the descriptor's `fallbackLabel` when decryption is unavailable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A decrypted value wider than its `plaintextType` was accepted silently — a 32-byte value declared `uint64` would render as a wildly wrong amount. Treat it as a failed decryption instead, so the field falls back to its placeholder. Leading zeros stay insignificant for integers, addresses and bools, so a wallet returning a zero-padded 32-byte ABI word is still accepted; `bytesN` compares raw length since every byte is part of the value. Folds the width into `parsePlaintextType` (renamed from `plaintextTypeToFieldType`) so the type string is parsed in one place. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
c8dda18 to
c17ac19
Compare
|
Addressed your comment and refined a few things |
|
I've made claude audit the PR and one issue has been found (+ 1 minor), could you have a look at it ? it looks legit to me: 🔴 Signed
|
`bytesSliceToFieldType` inferred an `intN`'s width from the returned byte length, so a positive value whose minimal encoding has the top bit set decoded as negative — an `int64` of 200 returned as `0xc8` rendered `-56`. The length is the value's own width for byte slices and ABI words, but a wallet picks it independently when returning a decrypted plaintext, so `decryptFieldValue` now passes the width the descriptor declares. Signed values must therefore be returned at their full declared width: `-1` as an `int64` is `0xffffffffffffffff`, not `0xff`, whose meaning depends on the width it is read at. Positives are unaffected either way. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Fixed in 572b84b. Confirmed the signed one — an One consequence: signed values must come back at their full declared width ( Left the minor one as-is: rejecting a sign-extended negative shows the fallback rather than a wrong number, and full width is the contract now anyway. Added a comment noting the intent. |
…scriptor Adds registry-level coverage for a zero-padded full ABI word, a top-bit-set uint64 (the unsigned counterpart of the signed-width bug, which this descriptor cannot exercise since it declares uint64), an over-wide plaintext, and a provider that declines — the last being a distinct cause from having no provider at all. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Implements the ERC-7730
encryptionfield annotation. Encrypted field values are decrypted by the wallet, then rendered with the field's regular format — anfhevm-encryptedbytes32amount becomes"1 cUSDC"instead of an opaque handle.ExternalDataProvider.resolveDecryptedValue— optional, scheme-specific callback. Decryption stays in the wallet because schemes need a live connection, a user signature, and an access-control check. Returns the plaintext as 0x-hex of its big-endian bytes; the library re-interprets those bytes via the descriptor's declaredplaintextType, so one encoding covers every scheme and type.fields.ts) — decrypts before visibility evaluation and rendering, soifNotIn/mustMatchand the format handler all see the plaintext. AddsplaintextTypeToFieldType(canonical Solidity type →FieldType), reusing the existingbytesSliceToFieldTypefor the bytes → typed coercion.fallbackLabel(or a generic[Encrypted]) with aDECRYPTION_FAILEDwarning rather than being dropped. The ciphertext is never shown as the value. A malformedencryptionannotation is instead fatal (INVALID_DESCRIPTOR), consistent with the other descriptor checks.DisplayField.rawEncryptedValue— the raw encrypted value, reported whether or not decryption succeeded. The spec RECOMMENDS wallets show it beside the placeholder.DecryptedValueResult,DescriptorFieldEncryptionScheme, and theDECRYPTION_FAILEDwarning code.ConfidentialWrapperregistry case using the descriptor and calldata from registry#2595 (whose own test cases only cover the fallback path), plus unit tests for the coercion and both failure branches.DECRYPTION.mdcovering the scheme-agnostic contract and thefhevm(Zama Protocol) integration, the only scheme ERC-7730 currently defines.src/,README.md, andGUIDE.mdstay scheme-agnostic.Also fixes stale
AGENTS.mdclaims found along the way:calldatawas listed as unimplemented (it isn't), andeip712.tswas credited with aresolveFieldTypethat doesn't exist.🤖 Generated with Claude Code