-
Notifications
You must be signed in to change notification settings - Fork 235
patch/non-evm-support-devtools-move #1779
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+159
−83
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
2453915
chore: baseX decoder is independent of eid
shankars99 8ef468d
chore: use baseX in wire move
shankars99 ae8d07e
refac: update imports
shankars99 de21ec3
chore: remove base64 encoding, log warning on format overlay (#1780)
tinom9 6f0b4f5
pnpm changeset
shankars99 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| --- | ||
| "@layerzerolabs/devtools-move": patch | ||
| "@layerzerolabs/oft-move": patch | ||
| --- | ||
|
|
||
| baseX converter does not depend on chainType |
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| import { expect } from 'chai' | ||
| import { randomBytes } from 'crypto' | ||
| import { basexToBytes32 } from '../tasks/shared/basexToBytes32' | ||
|
|
||
| describe('basexToBytes32 - Address Format Detection and Conversion', () => { | ||
| describe('Base16 format (0x prefix)', () => { | ||
| // Iterative test for random hex inputs from 1 to 32 bytes | ||
| for (let bytes = 1; bytes <= 32; bytes++) { | ||
| it(`should handle random ${bytes}-byte hex string`, () => { | ||
| const randomHex = '0x' + randomBytes(bytes).toString('hex') | ||
| const result = basexToBytes32(randomHex) | ||
|
|
||
| // Should always return 32-byte padded result for EVM addresses | ||
| expect(result).to.match(/^0x[a-f0-9]{64}$/) | ||
| expect(result).to.have.length(66) // 0x + 64 hex chars | ||
| }) | ||
| } | ||
| }) | ||
|
|
||
| describe('Base58 format', () => { | ||
| it('should convert solana address to bytes32', () => { | ||
| const address = '76y77prsiCMvXMjuoZ5VRrhG5qYBrUMYTE5WgHqgjEn6' | ||
| const bytes32 = basexToBytes32(address) | ||
| // Note: This will need to be updated with the actual expected bytes32 value | ||
| expect(bytes32).to.equal('0x5aad76da514b6e1dcf11037e904dac3d375f525c9fbafcb19507b78907d8c18b') | ||
| }) | ||
| }) | ||
|
|
||
| describe('Error handling', () => { | ||
| it('should throw error for unsupported format', () => { | ||
| const address = 'invalid-address-format' | ||
| expect(() => basexToBytes32(address)).to.throw(`Unsupported address format: ${address}`) | ||
| }) | ||
|
|
||
| it('should handle empty string', () => { | ||
| const address = '' | ||
| expect(() => basexToBytes32(address)).to.throw('Empty address provided') | ||
| }) | ||
| }) | ||
| }) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,27 +1,104 @@ | ||
| import basex from 'base-x' | ||
| import { ethers } from 'ethers' | ||
| import { ChainType, endpointIdToChainType } from '@layerzerolabs/lz-definitions' | ||
|
|
||
| const BASE58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' | ||
| type AddressFormat = 'hex' | 'base58' | ||
|
|
||
| export function basexToBytes32(basexAddress: string, eid: string): string { | ||
| const chainType = endpointIdToChainType(Number(eid)) | ||
| const BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' | ||
|
|
||
| if (chainType === ChainType.EVM) { | ||
| const addressBytes = ethers.utils.zeroPad(basexAddress, 32) | ||
| return `0x${Buffer.from(addressBytes).toString('hex')}` | ||
| } else if (chainType === ChainType.APTOS) { | ||
| return basexAddress | ||
| } else if (chainType === ChainType.SOLANA) { | ||
| return decodeSolanaAddress(basexAddress) | ||
| } else if (chainType === ChainType.INITIA) { | ||
| return basexAddress | ||
| } else { | ||
| throw new Error('Invalid chain type') | ||
| /** | ||
| * Converts any address format to a consistent bytes32 hex string. | ||
| * Auto-detects the input format and converts to left-padded 32-byte hex. | ||
| * | ||
| * @param address - Address in any supported format | ||
| * @returns 32-byte hex string with 0x prefix | ||
| * | ||
| * @example | ||
| * basexToBytes32('0x1234...') // Hex (EVM, etc...) (padded to 32 bytes) | ||
| * basexToBytes32('9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM') // Solana base58 (padded to 32 bytes) | ||
| * basexToBytes32('1234abcd') // Raw hex without 0x (padded to 32 bytes) | ||
| */ | ||
| export function basexToBytes32(address: string): string { | ||
| const bytes = detectAndDecodeAddress(address) | ||
| const paddedBytes = ethers.utils.zeroPad(bytes, 32) | ||
| return `0x${Buffer.from(paddedBytes).toString('hex')}` | ||
| } | ||
|
|
||
| /** | ||
| * Auto-detects address format and decodes to bytes. | ||
| * | ||
| * Detection algorithm (in order of priority): | ||
| * 1. Hex character set (with or without 0x) → Raw hex format | ||
| * 2. Base58 character set + typical length → Base58 format (Solana, etc.) | ||
| * | ||
| * Note: When formats overlap, the most specific format is used. | ||
| * | ||
| * @param address - Address string to decode | ||
| * @returns Uint8Array of decoded bytes | ||
| * @throws Error if address is empty or unsupported format | ||
| */ | ||
| function detectAndDecodeAddress(address: string): Uint8Array { | ||
| const cleanAddress = address.trim() | ||
| if (cleanAddress.length === 0) { | ||
| throw new Error('Empty address provided') | ||
| } | ||
| const isFormatMap: Record<AddressFormat, boolean> = { | ||
| hex: isHex(cleanAddress), | ||
| base58: isBase58(cleanAddress), | ||
| } | ||
| const validFormats: AddressFormat[] = Object.keys(isFormatMap).filter( | ||
| (key) => isFormatMap[key as AddressFormat] | ||
| ) as AddressFormat[] | ||
| if (validFormats.length === 0) { | ||
| throw new Error(`Unsupported address format: ${address}`) | ||
| } else if (validFormats.length > 1) { | ||
| console.warn( | ||
| `Address ${address} is valid for multiple formats: ${validFormats.join(', ')}, ` + | ||
| `it will be interpreted as the most specific format: ${validFormats[0]}` | ||
| ) | ||
| } | ||
| const format = validFormats[0] | ||
| return decodeWithFormat(cleanAddress, format) | ||
| } | ||
|
|
||
| /** | ||
| * Decodes address with explicit format specification. | ||
| */ | ||
| function decodeWithFormat(address: string, format: AddressFormat): Uint8Array { | ||
| try { | ||
| switch (format) { | ||
| case 'hex': { | ||
| const hexAddress = address.startsWith('0x') ? address : `0x${address}` | ||
| return ethers.utils.arrayify(hexAddress) | ||
| } | ||
| case 'base58': { | ||
| return basex(BASE58_ALPHABET).decode(address) | ||
| } | ||
| default: | ||
| throw new Error(`Unknown format: ${format}`) | ||
| } | ||
| } catch (error) { | ||
| throw new Error( | ||
| `Failed to decode address as ${format}: ${error instanceof Error ? error.message : String(error)}` | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Checks if a string is valid hexadecimal. | ||
| */ | ||
| function isHex(str: string): boolean { | ||
| if (str.startsWith('0x')) { | ||
| str = str.slice(2) | ||
| } | ||
| const hexRegex = /^[0-9a-fA-F]+$/ | ||
| return hexRegex.test(str) && str.length > 0 && str.length % 2 === 0 | ||
| } | ||
|
|
||
| export function decodeSolanaAddress(address: string): string { | ||
| const addressBytes = basex(BASE58).decode(address) | ||
| return '0x' + Buffer.from(addressBytes).toString('hex') | ||
| /** | ||
| * Checks if a string is valid base58. | ||
| * Uses regex for performance and consistency with other validation functions. | ||
| */ | ||
| function isBase58(str: string): boolean { | ||
| const base58Regex = new RegExp(`^[${BASE58_ALPHABET}]+$`) | ||
| return base58Regex.test(str) && str.length > 0 | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.