Skip to content

Commit 56e7ebb

Browse files
committed
base58
1 parent 6da40ac commit 56e7ebb

File tree

2 files changed

+91
-0
lines changed

2 files changed

+91
-0
lines changed

src/utils/Base58.sol

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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+
}

test/Base58.t.sol

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.4;
3+
4+
import "./utils/SoladyTest.sol";
5+
import {Base58} from "../src/utils/Base58.sol";
6+
import {LibString} from "../src/utils/LibString.sol";
7+
8+
contract Base58Test is SoladyTest {
9+
function testBase58Encode() public {
10+
// 0x000000000000000000001e3c8bf2dd5877fc13a2456ad7a584fe1629499985d685d6bf2e2983334225ec9dec91a445ce4f940fa4e75c3eee0436561dafe334
11+
bytes memory b =
12+
hex"00001e3c8bf2dd5877fc13a2456ad7a584fe1629499985d6bf2e2983334225ec9dec91a445ce4f940fa4e75c3eee0436561dafe334ef0886895d9ce60d812a18d92ead188c4e550a9479f9e83765d603c1c0ee6b2142457b3407b2ec756faabb";
13+
string memory encoded = Base58.encode(b);
14+
emit LogString(encoded);
15+
}
16+
}

0 commit comments

Comments
 (0)