|
| 1 | +// SPDX-License-Identifier: MIT |
| 2 | +pragma solidity ^0.8.4; |
| 3 | + |
| 4 | +/// @notice Library to encode strings in Base58. |
| 5 | +/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Base58.sol) |
| 6 | +/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Base58.sol) |
| 7 | +library Base58 { |
| 8 | + /// @dev Encodes `data` into a base58 string. |
| 9 | + function encode(bytes memory data) internal pure returns (string memory result) { |
| 10 | + /// @solidity memory-safe-assembly |
| 11 | + assembly { |
| 12 | + let l := mload(data) // `data.length`. |
| 13 | + let b := add(data, 0x20) // Start of `data` bytes. |
| 14 | + let z := 0 // Number of leading zero bytes in `data`. |
| 15 | + for {} lt(byte(0, mload(add(b, z))), lt(z, l)) {} { z := add(1, z) } |
| 16 | + |
| 17 | + // Start the output offset by an over-estimate of the length. |
| 18 | + let o := add(add(mload(0x40), 0x21), add(z, div(mul(sub(l, z), 8351), 6115))) |
| 19 | + let e := o |
| 20 | + |
| 21 | + let limbs := o |
| 22 | + let limbsEnd := limbs |
| 23 | + |
| 24 | + for { |
| 25 | + let i := mod(l, 31) |
| 26 | + if i { |
| 27 | + mstore(limbsEnd, shr(shl(3, add(1, sub(31, i))), mload(b))) |
| 28 | + limbsEnd := add(limbsEnd, 0x20) |
| 29 | + } |
| 30 | + } lt(i, l) { i := add(i, 31) } { |
| 31 | + mstore(limbsEnd, shr(8, mload(add(b, i)))) |
| 32 | + limbsEnd := add(limbsEnd, 0x20) |
| 33 | + } |
| 34 | + |
| 35 | + // Use the scratch space for the lookup. We'll restore 0x40 later. |
| 36 | + mstore(0x1f, "123456789ABCDEFGHJKLMNPQRSTUVWXY") |
| 37 | + mstore(0x3f, "Zabcdefghijkmnopqrstuvwxyz") |
| 38 | + |
| 39 | + if iszero(eq(limbs, limbsEnd)) { |
| 40 | + for {} 1 {} { |
| 41 | + let anyNonZero := 0 |
| 42 | + for { let i := limbs } 1 {} { |
| 43 | + if mload(i) { |
| 44 | + anyNonZero := 1 |
| 45 | + break |
| 46 | + } |
| 47 | + i := add(i, 0x20) |
| 48 | + if eq(i, limbsEnd) { break } |
| 49 | + } |
| 50 | + if iszero(anyNonZero) { break } |
| 51 | + |
| 52 | + let carry := 0 |
| 53 | + for { let i := limbs } 1 {} { |
| 54 | + let acc := add(shl(248, carry), mload(i)) |
| 55 | + mstore(i, div(acc, 58)) |
| 56 | + carry := mod(acc, 58) |
| 57 | + i := add(i, 0x20) |
| 58 | + if eq(i, limbsEnd) { break } |
| 59 | + } |
| 60 | + o := sub(o, 1) |
| 61 | + mstore8(o, mload(carry)) |
| 62 | + } |
| 63 | + for { let i := 0 } iszero(eq(i, z)) { i := add(i, 1) } { |
| 64 | + o := sub(o, 1) |
| 65 | + mstore8(o, 49) // '1' in ASCII. |
| 66 | + } |
| 67 | + } |
| 68 | + let n := sub(e, o) // Compute the final length. |
| 69 | + result := sub(o, 0x20) // Move back one word for the length. |
| 70 | + mstore(result, n) // Store the length. |
| 71 | + mstore(add(add(result, 0x20), n), 0) // Zeroize the slot after the bytes. |
| 72 | + mstore(0x40, add(add(result, 0x40), n)) // Allocate memory. |
| 73 | + } |
| 74 | + } |
| 75 | +} |
0 commit comments