function encode(private_key, public_key, memo, testNonce) {
    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');
    assert(public_key, 'public_key is required');
    checkEncryption();

    private_key = toPrivateObj(private_key);
    public_key = toPublicObj(public_key);

    const mbuf = new ByteBuffer(ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN);
    mbuf.writeVString(memo);
    memo = new Buffer.from(mbuf.copy(0, mbuf.offset).toBinary(), 'binary');

    const {nonce, message, checksum} = Aes.encrypt(private_key, public_key, memo, testNonce);
    memo = encMemo.fromObject({
        from: private_key.toPublicKey(),
        to: public_key,
        nonce,
        check: checksum,
        encrypted: message
    });
    // serialize
    memo = encMemo.toBuffer(memo);
    return '#' + base58.encode(new Buffer.from(memo, 'binary'));
}
Some fields are only required if the memo is marked for encryption (starts with a hash).
    @arg {string|PrivateKey} private_key - WIF or PrivateKey object
    @arg {string|PublicKey} public_key - Recipient
    @arg {string} memo - plain text is returned, hash prefix text is encrypted
    @arg {string} [testNonce = undefined] - just for testing
    @return {string} - base64 decoded string (or plain text)