fix: ensure recipients on Sealevel warp transfers are 32 bytes (#4760)

### Description

Ran into an issue trying to send from Sealevel -> Cosmos where the
recipient was 20 bytes. This hasn't been an issue so far because:
- no previous Sealevel -> Cosmos transfers have been done in the UI
- Sealevel -> EVM transfers are successful bc `addressToBytes` actually
pads the 20 byte EVM addresses to 32 bytes
- Sealevel -> Sealevel transfers are fine because Sealevel addys are
always 32 bytes

### Drive-by changes

<!--
Are there any minor or drive-by changes also included?
-->

### Related issues

<!--
- Fixes #[issue number here]
-->

### Backward compatibility

<!--
Are these changes backward compatible? Are there any infrastructure
implications, e.g. changes that would prohibit deploying older commits
using this infra tooling?

Yes/No
-->

### Testing

<!--
What kind of testing have these changes undergone?

None/Manual/Unit Tests
-->
pull/4751/head
Trevor Porter 4 days ago committed by GitHub
parent a36fc5fb27
commit 5fd4267e79
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 6
      .changeset/olive-maps-applaud.md
  2. 3
      typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts
  3. 12
      typescript/utils/src/addresses.test.ts
  4. 8
      typescript/utils/src/addresses.ts
  5. 1
      typescript/utils/src/index.ts

@ -0,0 +1,6 @@
---
'@hyperlane-xyz/utils': patch
'@hyperlane-xyz/sdk': patch
---
Supported non-32 byte non-EVM recipients when sending warps from Sealevel

@ -21,6 +21,7 @@ import {
addressToBytes, addressToBytes,
eqAddress, eqAddress,
median, median,
padBytesToLength,
} from '@hyperlane-xyz/utils'; } from '@hyperlane-xyz/utils';
import { BaseSealevelAdapter } from '../../app/MultiProtocolApp.js'; import { BaseSealevelAdapter } from '../../app/MultiProtocolApp.js';
@ -295,7 +296,7 @@ export abstract class SealevelHypTokenAdapter
instruction: SealevelHypTokenInstruction.TransferRemote, instruction: SealevelHypTokenInstruction.TransferRemote,
data: new SealevelTransferRemoteInstruction({ data: new SealevelTransferRemoteInstruction({
destination_domain: destination, destination_domain: destination,
recipient: addressToBytes(recipient), recipient: padBytesToLength(addressToBytes(recipient), 32),
amount_or_id: BigInt(weiAmountOrId), amount_or_id: BigInt(weiAmountOrId),
}), }),
}); });

@ -4,6 +4,7 @@ import {
addressToBytes, addressToBytes,
bytesToProtocolAddress, bytesToProtocolAddress,
isZeroishAddress, isZeroishAddress,
padBytesToLength,
} from './addresses.js'; } from './addresses.js';
import { ProtocolType } from './types.js'; import { ProtocolType } from './types.js';
@ -42,6 +43,17 @@ describe('Address utilities', () => {
}); });
}); });
describe('padBytesToLength', () => {
it('Pads bytes to a given length', () => {
const bytes = Buffer.from([1, 2, 3]);
expect(padBytesToLength(bytes, 5).equals(Buffer.from([0, 0, 1, 2, 3])));
});
it('Rejects bytes that exceed the target length', () => {
const bytes = Buffer.from([1, 2, 3]);
expect(() => padBytesToLength(bytes, 2)).to.throw(Error);
});
});
describe('bytesToProtocolAddress', () => { describe('bytesToProtocolAddress', () => {
it('Converts bytes to address', () => { it('Converts bytes to address', () => {
expect( expect(

@ -316,6 +316,14 @@ export function bytesToBytes32(bytes: Uint8Array): string {
); );
} }
// Pad bytes to a certain length, padding with 0s at the start
export function padBytesToLength(bytes: Uint8Array, length: number) {
if (bytes.length > length) {
throw new Error(`bytes must be ${length} bytes or less`);
}
return Buffer.concat([Buffer.alloc(length - bytes.length), bytes]);
}
export function bytesToAddressEvm(bytes: Uint8Array): Address { export function bytesToAddressEvm(bytes: Uint8Array): Address {
return bytes32ToAddress(Buffer.from(bytes).toString('hex')); return bytes32ToAddress(Buffer.from(bytes).toString('hex'));
} }

@ -35,6 +35,7 @@ export {
normalizeAddressCosmos, normalizeAddressCosmos,
normalizeAddressEvm, normalizeAddressEvm,
normalizeAddressSealevel, normalizeAddressSealevel,
padBytesToLength,
shortenAddress, shortenAddress,
strip0x, strip0x,
} from './addresses.js'; } from './addresses.js';

Loading…
Cancel
Save