diff --git a/src/system-contracts.ts b/src/system-contracts.ts index 88067a1..b6665e4 100644 --- a/src/system-contracts.ts +++ b/src/system-contracts.ts @@ -177,10 +177,9 @@ export function encodeSetGuardiansCalldata( threshold: number, timelock: number, ): HexString { - // Decode shell addresses to raw 32 bytes, then to 0x-hex for ABI encoding. - const hexGuardians = guardians.map(shellAddressToEvmHex); + const hexGuardians = guardians.map(shellAddressToNativeHex); const encoded = encodeAbiParameters( - [{ type: "address[]" }, { type: "uint8" }, { type: "uint64" }], + [{ type: "bytes32[]" }, { type: "uint8" }, { type: "uint64" }], [hexGuardians, threshold, BigInt(timelock)], ); return `${setGuardiansSelector}${encoded.slice(2)}` as HexString; @@ -213,9 +212,9 @@ export function encodeSubmitRecoveryCalldata( newAlgo: number, ): HexString { validateAccountPublicKey(newPubkey); - const hexAccount = shellAddressToEvmHex(account); + const hexAccount = shellAddressToNativeHex(account); const encoded = encodeAbiParameters( - [{ type: "address" }, { type: "bytes" }, { type: "uint8" }], + [{ type: "bytes32" }, { type: "bytes" }, { type: "uint8" }], [hexAccount, bytesToHex(newPubkey), newAlgo], ); return `${submitRecoverySelector}${encoded.slice(2)}` as HexString; @@ -231,8 +230,8 @@ export function encodeSubmitRecoveryCalldata( * @returns `HexString` with the 4-byte selector prepended. */ export function encodeExecuteRecoveryCalldata(account: AddressLike): HexString { - const hexAccount = shellAddressToEvmHex(account); - const encoded = encodeAbiParameters([{ type: "address" }], [hexAccount]); + const hexAccount = shellAddressToNativeHex(account); + const encoded = encodeAbiParameters([{ type: "bytes32" }], [hexAccount]); return `${executeRecoverySelector}${encoded.slice(2)}` as HexString; } @@ -246,8 +245,8 @@ export function encodeExecuteRecoveryCalldata(account: AddressLike): HexString { * @returns `HexString` with the 4-byte selector prepended. */ export function encodeCancelRecoveryCalldata(account: AddressLike): HexString { - const hexAccount = shellAddressToEvmHex(account); - const encoded = encodeAbiParameters([{ type: "address" }], [hexAccount]); + const hexAccount = shellAddressToNativeHex(account); + const encoded = encodeAbiParameters([{ type: "bytes32" }], [hexAccount]); return `${cancelRecoverySelector}${encoded.slice(2)}` as HexString; } @@ -255,7 +254,7 @@ export function encodeCancelRecoveryCalldata(account: AddressLike): HexString { // Internal helpers // --------------------------------------------------------------------------- -/** Convert a native 32-byte Shell address to its 20-byte EVM ABI representation. */ -function shellAddressToEvmHex(address: AddressLike): `0x${string}` { - return bytesToHex(shellAddressToBytes(address as string).slice(-20)); +/** Preserve the native address in the 32-byte ABI word decoded by AccountManager. */ +function shellAddressToNativeHex(address: AddressLike): `0x${string}` { + return bytesToHex(shellAddressToBytes(address as string)); } diff --git a/tests/system-contracts.test.mjs b/tests/system-contracts.test.mjs index b0c4894..04075d9 100644 --- a/tests/system-contracts.test.mjs +++ b/tests/system-contracts.test.mjs @@ -34,7 +34,12 @@ test("account public-key encoders reject empty payloads", () => { }); test("guardian recovery encoders accept native 32-byte Shell addresses", () => { - assert.match(encodeSetGuardiansCalldata([account], 1, 100), /^0x/); - assert.match(encodeExecuteRecoveryCalldata(account), /^0x/); - assert.match(encodeCancelRecoveryCalldata(account), /^0x/); + const nativeWord = "11".repeat(32); + assert.ok(encodeSetGuardiansCalldata([account], 1, 100).endsWith(nativeWord)); + assert.equal( + encodeSubmitRecoveryCalldata(account, new Uint8Array([1]), 1).slice(10, 74), + nativeWord, + ); + assert.equal(encodeExecuteRecoveryCalldata(account).slice(10, 74), nativeWord); + assert.equal(encodeCancelRecoveryCalldata(account).slice(10, 74), nativeWord); });