Forráskód Böngészése

Add utility function for converting an address to checksummed string (#5067)

cairo 1 éve
szülő
commit
337bfd5ea4

+ 5 - 0
.changeset/forty-dodos-visit.md

@@ -0,0 +1,5 @@
+---
+'openzeppelin-solidity': minor
+---
+
+`Strings`: Added a utility function for converting an address to checksummed string.

+ 24 - 0
contracts/utils/Strings.sol

@@ -85,6 +85,30 @@ library Strings {
         return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
     }
 
+    /**
+     * @dev Converts an `address` with fixed length of 20 bytes to its checksummed ASCII `string` hexadecimal
+     * representation, according to EIP-55.
+     */
+    function toChecksumHexString(address addr) internal pure returns (string memory) {
+        bytes memory buffer = bytes(toHexString(addr));
+
+        // hash the hex part of buffer (skip length + 2 bytes, length 40)
+        uint256 hashValue;
+        assembly ("memory-safe") {
+            hashValue := shr(96, keccak256(add(buffer, 0x22), 40))
+        }
+
+        for (uint256 i = 41; i > 1; --i) {
+            // possible values for buffer[i] are 48 (0) to 57 (9) and 97 (a) to 102 (f)
+            if (hashValue & 0xf > 7 && uint8(buffer[i]) > 96) {
+                // case shift by xoring with 0x20
+                buffer[i] ^= 0x20;
+            }
+            hashValue >>= 4;
+        }
+        return string(buffer);
+    }
+
     /**
      * @dev Returns true if the two strings are equal.
      */

+ 34 - 7
test/utils/Strings.test.js

@@ -108,15 +108,42 @@ describe('Strings', function () {
     });
   });
 
-  describe('toHexString address', function () {
-    it('converts a random address', async function () {
-      const addr = '0xa9036907dccae6a1e0033479b12e837e5cf5a02f';
-      expect(await this.mock.getFunction('$toHexString(address)')(addr)).to.equal(addr);
+  describe('addresses', function () {
+    const addresses = [
+      '0xa9036907dccae6a1e0033479b12e837e5cf5a02f', // Random address
+      '0x0000e0ca771e21bd00057f54a68c30d400000000', // Leading and trailing zeros
+      // EIP-55 reference
+      '0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed',
+      '0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359',
+      '0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB',
+      '0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb',
+      '0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359',
+      '0x52908400098527886E0F7030069857D2E4169EE7',
+      '0x8617E340B3D01FA5F11F306F4090FD50E238070D',
+      '0xde709f2102306220921060314715629080e2fb77',
+      '0x27b1fdb04752bbc536007a920d24acb045561c26',
+      '0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed',
+      '0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359',
+      '0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB',
+      '0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb',
+    ];
+
+    describe('toHexString', function () {
+      for (const addr of addresses) {
+        it(`converts ${addr}`, async function () {
+          expect(await this.mock.getFunction('$toHexString(address)')(addr)).to.equal(addr.toLowerCase());
+        });
+      }
     });
 
-    it('converts an address with leading zeros', async function () {
-      const addr = '0x0000e0ca771e21bd00057f54a68c30d400000000';
-      expect(await this.mock.getFunction('$toHexString(address)')(addr)).to.equal(addr);
+    describe('toChecksumHexString', function () {
+      for (const addr of addresses) {
+        it(`converts ${addr}`, async function () {
+          expect(await this.mock.getFunction('$toChecksumHexString(address)')(addr)).to.equal(
+            ethers.getAddress(addr.toLowerCase()),
+          );
+        });
+      }
     });
   });