Skip to content

Commit d4b2fea

Browse files
authored
Fix infinite loop in address parsing (#746)
* fix potential inifinite loop * simple example file showing how to parse addresses * toNative can handle a UniversalAddress value * add unit tests to document and test this behavior * handle Uint8Array too * check it starts with 0x * test
1 parent 9105de2 commit d4b2fea

File tree

6 files changed

+62
-6
lines changed

6 files changed

+62
-6
lines changed

core/definitions/__tests__/address.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,4 @@ describe("UniversalAddress tests", function () {
3535
const ua = new UniversalAddress(appId, "algorandAppId");
3636
expect(ua.toString()).toEqual(appAddress)
3737
});
38-
});
38+
});

core/definitions/src/address.ts

+14-3
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,20 @@ export function toNative<C extends Chain>(
102102
}
103103

104104
return nativeAddress;
105-
} catch (_) {
106-
// try to parse it as a universal address
107-
return (UniversalAddress.instanceof(ua) ? ua : new UniversalAddress(ua)).toNative(chain);
105+
} catch (e: any) {
106+
const err = `Error parsing address as a native ${chain} address: ${e.message}`;
107+
108+
if (UniversalAddress.instanceof(ua)) {
109+
throw err;
110+
} else {
111+
// If we were given a string or Uint8Array value, which is ambiguously either a
112+
// NativeAddress or UniversalAddress, and it failed to parse directly
113+
// as a NativeAddress, we try one more time to parse it as a UniversalAddress
114+
// first and then convert that to a NativeAddress.
115+
console.error(err);
116+
console.error('Attempting to parse as UniversalAddress');
117+
return (new UniversalAddress(ua)).toNative(chain);
118+
}
108119
}
109120
}
110121

core/definitions/src/universalAddress.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export class UniversalAddress implements Address {
2626
}
2727

2828
toNative<T extends Parameters<typeof toNative>[0]>(chainOrPlatform: T): NativeAddress<T> {
29-
return toNative(chainOrPlatform, this.toUint8Array());
29+
return toNative(chainOrPlatform, this);
3030
}
3131

3232
unwrap(): Uint8Array {

examples/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"wrapped": "tsx src/createWrapped.ts",
3838
"tb": "tsx src/tokenBridge.ts",
3939
"cctp": "tsx src/cctp.ts",
40+
"parseAddress": "tsx src/parseAddress.ts",
4041
"demo": "tsx src/index.ts",
4142
"cosmos": "tsx src/cosmos.ts",
4243
"msg": "tsx src/messaging.ts",
@@ -53,4 +54,4 @@
5354
"dependencies": {
5455
"@wormhole-foundation/sdk": "1.0.3"
5556
}
56-
}
57+
}

examples/src/parseAddress.ts

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { toNative, toUniversal } from "@wormhole-foundation/sdk";
2+
3+
const ETHEREUM_ADDRESS = '0xaaee1a9723aadb7afa2810263653a34ba2c21c7a';
4+
const ETHEREUM_ADDRESS_UNIVERSAL = toUniversal('Ethereum', ETHEREUM_ADDRESS).toString();
5+
6+
(async function () {
7+
// We can parse an Ethereum address from its native or universal format
8+
const parsedEthereumAddr1 = toNative('Ethereum', ETHEREUM_ADDRESS);
9+
const parsedEthereumAddr2 = toNative('Ethereum', ETHEREUM_ADDRESS_UNIVERSAL);
10+
console.log(parsedEthereumAddr1);
11+
console.log(parsedEthereumAddr2);
12+
13+
// Parsing a Sui address as Ethereum will throw:
14+
try {
15+
toNative('Ethereum', '0xabd62c91e3bd89243c592b93b9f45cf9f584be3df4574e05ae31d02fcfef67fc');
16+
} catch (e) {
17+
console.error(e);
18+
}
19+
})();
20+

platforms/evm/__tests__/unit/platform.test.ts

+24
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ import {
2020
chains,
2121
} from '@wormhole-foundation/sdk-connect';
2222

23+
import {
24+
toNative,
25+
} from '@wormhole-foundation/sdk-definitions';
26+
2327
import '@wormhole-foundation/sdk-evm-core';
2428
import '@wormhole-foundation/sdk-evm-tokenbridge';
2529
import { EvmPlatform } from '../../src/platform.js';
@@ -38,6 +42,26 @@ const configs = CONFIG[network].chains;
3842
// const satisfiesInterface: PlatformUtils<typeof network> = EvmPlatform;
3943

4044
describe('EVM Platform Tests', () => {
45+
describe("Parse Ethereum address", function () {
46+
test("should correctly parse Ethereum addresses", () => {
47+
expect(() =>
48+
toNative('Ethereum', '0xaaee1a9723aadb7afa2810263653a34ba2c21c7a')
49+
).toBeTruthy();
50+
});
51+
52+
test("should correctly handle zero-padded Ethereum addresses (in universal address format)", () => {
53+
expect(() =>
54+
toNative('Ethereum', '0x000000000000000000000000aaee1a9723aadb7afa2810263653a34ba2c21c7a')
55+
).toBeTruthy();
56+
});
57+
58+
test("should throw when parsing an invalid Ethereum addresses", () => {
59+
expect(() =>
60+
toNative('Ethereum', '0xabd62c91e3bd89243c592b93b9f45cf9f584be3df4574e05ae31d02fcfef67fc')
61+
).toThrow();
62+
});
63+
});
64+
4165
describe('Get Token Bridge', () => {
4266
test('No RPC', async () => {
4367
const p = new EvmPlatform(network, {});

0 commit comments

Comments
 (0)