draft-InteroperableAddress.test.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. const { ethers } = require('hardhat');
  2. const { expect } = require('chai');
  3. const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
  4. const { addressCoder, nameCoder } = require('interoperable-addresses');
  5. const { CAIP350, chainTypeCoder } = require('interoperable-addresses/dist/CAIP350');
  6. const { getLocalChain } = require('../helpers/chains');
  7. async function fixture() {
  8. const mock = await ethers.deployContract('$InteroperableAddress');
  9. return { mock };
  10. }
  11. describe('ERC7390', function () {
  12. before(async function () {
  13. Object.assign(this, await loadFixture(fixture));
  14. });
  15. it('formatEvmV1 address on the local chain', async function () {
  16. const { reference: chainid, toErc7930 } = await getLocalChain();
  17. await expect(
  18. this.mock.$formatEvmV1(ethers.Typed.uint256(chainid), ethers.Typed.address(this.mock)),
  19. ).to.eventually.equal(toErc7930(this.mock));
  20. });
  21. it('formatV1 fails if both reference and address are empty', async function () {
  22. await expect(this.mock.$formatV1('0x0000', '0x', '0x')).to.be.revertedWithCustomError(
  23. this.mock,
  24. 'InteroperableAddressEmptyReferenceAndAddress',
  25. );
  26. });
  27. describe('reference examples', function () {
  28. for (const { title, name } of [
  29. {
  30. title: 'Example 1: Ethereum mainnet address',
  31. name: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045@eip155:1#4CA88C9C',
  32. },
  33. {
  34. title: 'Example 2: Solana mainnet address',
  35. name: 'MJKqp326RZCHnAAbew9MDdui3iCKWco7fsK9sVuZTX2@solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d#88835C11',
  36. },
  37. {
  38. title: 'Example 3: EVM address without chainid',
  39. name: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045@eip155#B26DB7CB',
  40. },
  41. {
  42. title: 'Example 4: Solana mainnet network, no address',
  43. name: '@solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d#2EB18670',
  44. },
  45. {
  46. title: 'Example 5: Arbitrum One address',
  47. name: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045@eip155:42161#D2E02854',
  48. },
  49. {
  50. title: 'Example 6: Ethereum mainnet, no address',
  51. name: '@eip155:1#F54D4FBF',
  52. },
  53. ]) {
  54. const { chainType, reference, address } = nameCoder.decode(name, true);
  55. const binary = addressCoder.encode({ chainType, reference, address });
  56. it(title, async function () {
  57. const expected = [
  58. chainTypeCoder.decode(chainType),
  59. CAIP350[chainType].reference.decode(reference),
  60. CAIP350[chainType].address.decode(address),
  61. ].map(ethers.hexlify);
  62. await expect(this.mock.$parseV1(binary)).to.eventually.deep.equal(expected);
  63. await expect(this.mock.$parseV1Calldata(binary)).to.eventually.deep.equal(expected);
  64. await expect(this.mock.$tryParseV1(binary)).to.eventually.deep.equal([true, ...expected]);
  65. await expect(this.mock.$tryParseV1Calldata(binary)).to.eventually.deep.equal([true, ...expected]);
  66. await expect(this.mock.$formatV1(...expected)).to.eventually.equal(binary);
  67. if (chainType == 'eip155') {
  68. await expect(this.mock.$parseEvmV1(binary)).to.eventually.deep.equal([
  69. reference ?? 0n,
  70. address ?? ethers.ZeroAddress,
  71. ]);
  72. await expect(this.mock.$parseEvmV1Calldata(binary)).to.eventually.deep.equal([
  73. reference ?? 0n,
  74. address ?? ethers.ZeroAddress,
  75. ]);
  76. await expect(this.mock.$tryParseEvmV1(binary)).to.eventually.deep.equal([
  77. true,
  78. reference ?? 0n,
  79. address ?? ethers.ZeroAddress,
  80. ]);
  81. await expect(this.mock.$tryParseEvmV1Calldata(binary)).to.eventually.deep.equal([
  82. true,
  83. reference ?? 0n,
  84. address ?? ethers.ZeroAddress,
  85. ]);
  86. if (!address) {
  87. await expect(this.mock.$formatEvmV1(ethers.Typed.uint256(reference))).to.eventually.equal(
  88. binary.toLowerCase(),
  89. );
  90. } else if (!reference) {
  91. await expect(this.mock.$formatEvmV1(ethers.Typed.address(address))).to.eventually.equal(
  92. binary.toLowerCase(),
  93. );
  94. } else {
  95. await expect(
  96. this.mock.$formatEvmV1(ethers.Typed.uint256(reference), ethers.Typed.address(address)),
  97. ).to.eventually.equal(binary.toLowerCase());
  98. }
  99. }
  100. });
  101. }
  102. });
  103. describe('invalid format', function () {
  104. for (const [title, binary] of Object.entries({
  105. // version 2 + some data
  106. 'unsupported version': '0x00020000010100',
  107. // version + ref: missing chainReferenceLength and addressLength
  108. 'too short (case 1)': '0x00010000',
  109. // version + ref + chainReference: missing addressLength
  110. 'too short (case 2)': '0x000100000101',
  111. // version + ref + chainReference + addressLength + part of the address: missing 2 bytes of the address
  112. 'too short (case 3)': '0x00010000010114d8da6bf26964af9d7eed9e03e53415d37aa9',
  113. })) {
  114. it(title, async function () {
  115. await expect(this.mock.$parseV1(binary))
  116. .to.be.revertedWithCustomError(this.mock, 'InteroperableAddressParsingError')
  117. .withArgs(binary);
  118. await expect(this.mock.$parseV1Calldata(binary))
  119. .to.be.revertedWithCustomError(this.mock, 'InteroperableAddressParsingError')
  120. .withArgs(binary);
  121. await expect(this.mock.$parseEvmV1(binary))
  122. .to.be.revertedWithCustomError(this.mock, 'InteroperableAddressParsingError')
  123. .withArgs(binary);
  124. await expect(this.mock.$parseEvmV1Calldata(binary))
  125. .to.be.revertedWithCustomError(this.mock, 'InteroperableAddressParsingError')
  126. .withArgs(binary);
  127. await expect(this.mock.$tryParseV1(binary)).to.eventually.deep.equal([false, '0x0000', '0x', '0x']);
  128. await expect(this.mock.$tryParseV1Calldata(binary)).to.eventually.deep.equal([false, '0x0000', '0x', '0x']);
  129. await expect(this.mock.$tryParseEvmV1(binary)).to.eventually.deep.equal([false, 0n, ethers.ZeroAddress]);
  130. await expect(this.mock.$tryParseEvmV1Calldata(binary)).to.eventually.deep.equal([
  131. false,
  132. 0n,
  133. ethers.ZeroAddress,
  134. ]);
  135. });
  136. }
  137. for (const [title, binary] of Object.entries({
  138. 'not an evm format: chainid too long':
  139. '0x00010000212dc7f03c13ad47809e88339107c33a612043d704c1c9693a74996e7f9c6bee8f2314d8da6bf26964af9d7eed9e03e53415d37aa96045',
  140. 'not an evm format: address in not 20 bytes': '0x00010000010112d8da6bf26964af9d7eed9e03e53415d37aa9',
  141. })) {
  142. it(title, async function () {
  143. await expect(this.mock.$parseEvmV1(binary))
  144. .to.be.revertedWithCustomError(this.mock, 'InteroperableAddressParsingError')
  145. .withArgs(binary);
  146. await expect(this.mock.$parseEvmV1Calldata(binary))
  147. .to.be.revertedWithCustomError(this.mock, 'InteroperableAddressParsingError')
  148. .withArgs(binary);
  149. await expect(this.mock.$tryParseEvmV1(binary)).to.eventually.deep.equal([false, 0n, ethers.ZeroAddress]);
  150. await expect(this.mock.$tryParseEvmV1Calldata(binary)).to.eventually.deep.equal([
  151. false,
  152. 0n,
  153. ethers.ZeroAddress,
  154. ]);
  155. });
  156. }
  157. });
  158. });