function decode(private_key, memo) {
    assert(memo, 'memo is required');
    assert.equal(typeof memo, 'string', 'memo');
    if(!/^#/.test(memo)) return memo;
    memo = memo.substring(1);

    assert(private_key, 'private_key is required');
    checkEncryption();

    private_key = toPrivateObj(private_key);

    memo = base58.decode(memo);
    memo = encMemo.fromBuffer(new Buffer.from(memo, 'binary'));

    const {from, to, nonce, check, encrypted} = memo;
    const pubkey = private_key.toPublicKey().toString();
    const otherpub = pubkey === from.toString() ? to.toString() : from.toString();
    memo = Aes.decrypt(private_key, otherpub, nonce, encrypted, check);

    // remove varint length prefix
    const mbuf = ByteBuffer.fromBinary(memo.toString('binary'), ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN);
    try {
        mbuf.mark();
        return '#' + mbuf.readVString();
    } catch(e) {
        mbuf.reset();
        // Sender did not length-prefix the memo
        memo = new Buffer.from(mbuf.toString('binary'), 'binary').toString('utf-8');
        return '#' + memo;
    }
}
Some fields are only required if the memo is marked for decryption (starts with a hash).
    @arg {string|PrivateKey} private_key - WIF or PrivateKey object
    @arg {string} memo - plain text is returned, hash prefix base58 is decrypted
    @return {string} - utf8 decoded string (hash prefix)