diff --git a/src/operation.js b/src/operation.js index 21373991f..e9de4c423 100644 --- a/src/operation.js +++ b/src/operation.js @@ -10,7 +10,6 @@ import isFinite from 'lodash/isFinite'; import { best_r } from './util/continued_fraction'; import { Asset } from './asset'; import { StrKey } from './strkey'; -import { Keypair } from './keypair'; import xdr from './generated/stellar-xdr_generated'; import * as ops from './operations/index'; @@ -62,12 +61,13 @@ export const AuthImmutableFlag = 1 << 2; export class Operation { static setSourceAccount(opAttributes, opts) { if (opts.source) { - if (!StrKey.isValidEd25519PublicKey(opts.source)) { + try { + opAttributes.sourceAccount = xdr.MuxedAccount.fromXDR( + StrKey.decodeMuxedAccount(opts.source) + ); + } catch (e) { throw new Error('Source address is invalid'); } - opAttributes.sourceAccount = Keypair.fromPublicKey( - opts.source - ).xdrMuxedAccount(); } } @@ -81,10 +81,13 @@ export class Operation { function accountIdtoAddress(accountId) { return StrKey.encodeEd25519PublicKey(accountId.ed25519()); } + function muxedAccounttoAddress(accountId) { + return StrKey.encodeMuxedAccount(accountId.toXDR()); + } const result = {}; if (operation.sourceAccount()) { - result.source = accountIdtoAddress(operation.sourceAccount()); + result.source = muxedAccounttoAddress(operation.sourceAccount()); } const attrs = operation.body().value(); @@ -99,7 +102,7 @@ export class Operation { } case 'payment': { result.type = 'payment'; - result.destination = accountIdtoAddress(attrs.destination()); + result.destination = muxedAccounttoAddress(attrs.destination()); result.asset = Asset.fromOperation(attrs.asset()); result.amount = this._fromXDRAmount(attrs.amount()); break; @@ -108,7 +111,7 @@ export class Operation { result.type = 'pathPaymentStrictReceive'; result.sendAsset = Asset.fromOperation(attrs.sendAsset()); result.sendMax = this._fromXDRAmount(attrs.sendMax()); - result.destination = accountIdtoAddress(attrs.destination()); + result.destination = muxedAccounttoAddress(attrs.destination()); result.destAsset = Asset.fromOperation(attrs.destAsset()); result.destAmount = this._fromXDRAmount(attrs.destAmount()); result.path = []; @@ -125,7 +128,7 @@ export class Operation { result.type = 'pathPaymentStrictSend'; result.sendAsset = Asset.fromOperation(attrs.sendAsset()); result.sendAmount = this._fromXDRAmount(attrs.sendAmount()); - result.destination = accountIdtoAddress(attrs.destination()); + result.destination = muxedAccounttoAddress(attrs.destination()); result.destAsset = Asset.fromOperation(attrs.destAsset()); result.destMin = this._fromXDRAmount(attrs.destMin()); result.path = []; @@ -230,7 +233,7 @@ export class Operation { } case 'accountMerge': { result.type = 'accountMerge'; - result.destination = accountIdtoAddress(attrs); + result.destination = muxedAccounttoAddress(attrs); break; } case 'manageDatum': { diff --git a/src/operations/account_merge.js b/src/operations/account_merge.js index 2d8a3ce66..23139018b 100644 --- a/src/operations/account_merge.js +++ b/src/operations/account_merge.js @@ -1,5 +1,4 @@ import xdr from '../generated/stellar-xdr_generated'; -import { Keypair } from '../keypair'; import { StrKey } from '../strkey'; /** @@ -13,12 +12,13 @@ import { StrKey } from '../strkey'; */ export function accountMerge(opts) { const opAttributes = {}; - if (!StrKey.isValidEd25519PublicKey(opts.destination)) { + try { + opAttributes.body = xdr.OperationBody.accountMerge( + xdr.MuxedAccount.fromXDR(StrKey.decodeMuxedAccount(opts.destination)) + ); + } catch (e) { throw new Error('destination is invalid'); } - opAttributes.body = xdr.OperationBody.accountMerge( - Keypair.fromPublicKey(opts.destination).xdrMuxedAccount() - ); this.setSourceAccount(opAttributes, opts); return new xdr.Operation(opAttributes); diff --git a/src/operations/path_payment_strict_receive.js b/src/operations/path_payment_strict_receive.js index 04e580e1e..9566ce131 100644 --- a/src/operations/path_payment_strict_receive.js +++ b/src/operations/path_payment_strict_receive.js @@ -1,5 +1,4 @@ import xdr from '../generated/stellar-xdr_generated'; -import { Keypair } from '../keypair'; import { StrKey } from '../strkey'; /** @@ -24,8 +23,6 @@ export function pathPaymentStrictReceive(opts) { throw new Error('Must specify a send asset'); case !this.isValidAmount(opts.sendMax): throw new TypeError(this.constructAmountRequirementsError('sendMax')); - case !StrKey.isValidEd25519PublicKey(opts.destination): - throw new Error('destination is invalid'); case !opts.destAsset: throw new Error('Must provide a destAsset for a payment operation'); case !this.isValidAmount(opts.destAmount): @@ -37,9 +34,15 @@ export function pathPaymentStrictReceive(opts) { const attributes = {}; attributes.sendAsset = opts.sendAsset.toXDRObject(); attributes.sendMax = this._toXDRAmount(opts.sendMax); - attributes.destination = Keypair.fromPublicKey( - opts.destination - ).xdrMuxedAccount(); + + try { + attributes.destination = xdr.MuxedAccount.fromXDR( + StrKey.decodeMuxedAccount(opts.destination) + ); + } catch (e) { + throw new Error('destination is invalid'); + } + attributes.destAsset = opts.destAsset.toXDRObject(); attributes.destAmount = this._toXDRAmount(opts.destAmount); diff --git a/src/operations/path_payment_strict_send.js b/src/operations/path_payment_strict_send.js index 47bbbd06a..c40852a96 100644 --- a/src/operations/path_payment_strict_send.js +++ b/src/operations/path_payment_strict_send.js @@ -1,5 +1,4 @@ import xdr from '../generated/stellar-xdr_generated'; -import { Keypair } from '../keypair'; import { StrKey } from '../strkey'; /** @@ -24,8 +23,6 @@ export function pathPaymentStrictSend(opts) { throw new Error('Must specify a send asset'); case !this.isValidAmount(opts.sendAmount): throw new TypeError(this.constructAmountRequirementsError('sendAmount')); - case !StrKey.isValidEd25519PublicKey(opts.destination): - throw new Error('destination is invalid'); case !opts.destAsset: throw new Error('Must provide a destAsset for a payment operation'); case !this.isValidAmount(opts.destMin): @@ -37,9 +34,13 @@ export function pathPaymentStrictSend(opts) { const attributes = {}; attributes.sendAsset = opts.sendAsset.toXDRObject(); attributes.sendAmount = this._toXDRAmount(opts.sendAmount); - attributes.destination = Keypair.fromPublicKey( - opts.destination - ).xdrMuxedAccount(); + try { + attributes.destination = xdr.MuxedAccount.fromXDR( + StrKey.decodeMuxedAccount(opts.destination) + ); + } catch (e) { + throw new Error('destination is invalid'); + } attributes.destAsset = opts.destAsset.toXDRObject(); attributes.destMin = this._toXDRAmount(opts.destMin); diff --git a/src/operations/payment.js b/src/operations/payment.js index 96253dbbb..5e9f2f8f2 100644 --- a/src/operations/payment.js +++ b/src/operations/payment.js @@ -1,5 +1,4 @@ import xdr from '../generated/stellar-xdr_generated'; -import { Keypair } from '../keypair'; import { StrKey } from '../strkey'; /** @@ -14,9 +13,6 @@ import { StrKey } from '../strkey'; * @returns {xdr.PaymentOp} Payment operation */ export function payment(opts) { - if (!StrKey.isValidEd25519PublicKey(opts.destination)) { - throw new Error('destination is invalid'); - } if (!opts.asset) { throw new Error('Must provide an asset for a payment operation'); } @@ -25,9 +21,14 @@ export function payment(opts) { } const attributes = {}; - attributes.destination = Keypair.fromPublicKey( - opts.destination - ).xdrMuxedAccount(); + try { + attributes.destination = xdr.MuxedAccount.fromXDR( + StrKey.decodeMuxedAccount(opts.destination) + ); + } catch (e) { + throw new Error('destination is invalid'); + } + attributes.asset = opts.asset.toXDRObject(); attributes.amount = this._toXDRAmount(opts.amount); const paymentOp = new xdr.PaymentOp(attributes); diff --git a/test/unit/operation_test.js b/test/unit/operation_test.js index 863f58b5a..2d029f16f 100644 --- a/test/unit/operation_test.js +++ b/test/unit/operation_test.js @@ -28,7 +28,6 @@ describe('Operation', function() { ).to.be.equal('10000000000'); expect(obj.startingBalance).to.be.equal(startingBalance); }); - it('fails to create createAccount operation with an invalid destination address', function() { let opts = { destination: 'GCEZW', @@ -80,15 +79,31 @@ describe('Operation', function() { var obj = StellarBase.Operation.fromXDRObject(operation); expect(obj.type).to.be.equal('payment'); expect(obj.destination).to.be.equal(destination); - expect( - operation - .body() - .value() - .amount() - .toString() - ).to.be.equal('10000000000'); - expect(obj.amount).to.be.equal(amount); - expect(obj.asset.equals(asset)).to.be.true; + }); + it('supports muxed accounts', function() { + var destination = + 'MAAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITLVL6'; + var amount = '1000.0000000'; + var asset = new StellarBase.Asset( + 'USDUSD', + 'GDGU5OAPHNPU5UCLE5RDJHG7PXZFQYWKCFOEXSXNMR6KRQRI5T6XXCD7' + ); + var source = + 'MAAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITLVL6'; + let op = StellarBase.Operation.payment({ + destination, + asset, + amount, + source + }); + var xdr = op.toXDR('hex'); + var operation = StellarBase.xdr.Operation.fromXDR( + Buffer.from(xdr, 'hex') + ); + var obj = StellarBase.Operation.fromXDRObject(operation); + expect(obj.type).to.be.equal('payment'); + expect(obj.destination).to.be.equal(destination); + expect(obj.source).to.be.equal(source); }); it('fails to create payment operation with an invalid destination address', function() { @@ -180,7 +195,48 @@ describe('Operation', function() { 'GDTNXRLOJD2YEBPKK7KCMR7J33AAG5VZXHAJTHIG736D6LVEFLLLKPDL' ); }); - + it('supports muxed accounts', function() { + var sendAsset = new StellarBase.Asset( + 'USD', + 'GDGU5OAPHNPU5UCLE5RDJHG7PXZFQYWKCFOEXSXNMR6KRQRI5T6XXCD7' + ); + var sendMax = '3.0070000'; + var destination = + 'MAAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITLVL6'; + var source = + 'MAAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITLVL6'; + var destAsset = new StellarBase.Asset( + 'USD', + 'GDGU5OAPHNPU5UCLE5RDJHG7PXZFQYWKCFOEXSXNMR6KRQRI5T6XXCD7' + ); + var destAmount = '3.1415000'; + var path = [ + new StellarBase.Asset( + 'USD', + 'GBBM6BKZPEHWYO3E3YKREDPQXMS4VK35YLNU7NFBRI26RAN7GI5POFBB' + ), + new StellarBase.Asset( + 'EUR', + 'GDTNXRLOJD2YEBPKK7KCMR7J33AAG5VZXHAJTHIG736D6LVEFLLLKPDL' + ) + ]; + let op = StellarBase.Operation.pathPaymentStrictReceive({ + sendAsset, + sendMax, + destination, + destAsset, + destAmount, + path, + source + }); + var xdr = op.toXDR('hex'); + var operation = StellarBase.xdr.Operation.fromXDR( + Buffer.from(xdr, 'hex') + ); + var obj = StellarBase.Operation.fromXDRObject(operation); + expect(obj.destination).to.be.equal(destination); + expect(obj.source).to.be.equal(destination); + }); it('fails to create path payment operation with an invalid destination address', function() { let opts = { destination: 'GCEZW', @@ -296,7 +352,45 @@ describe('Operation', function() { 'GDTNXRLOJD2YEBPKK7KCMR7J33AAG5VZXHAJTHIG736D6LVEFLLLKPDL' ); }); - + it('supports muxed accounts', function() { + var sendAsset = new StellarBase.Asset( + 'USD', + 'GDGU5OAPHNPU5UCLE5RDJHG7PXZFQYWKCFOEXSXNMR6KRQRI5T6XXCD7' + ); + var sendAmount = '3.0070000'; + var destination = + 'MAAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITLVL6'; + var source = + 'MAAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITLVL6'; + var destAsset = new StellarBase.Asset( + 'USD', + 'GDGU5OAPHNPU5UCLE5RDJHG7PXZFQYWKCFOEXSXNMR6KRQRI5T6XXCD7' + ); + var destMin = '3.1415000'; + var path = [ + new StellarBase.Asset( + 'USD', + 'GBBM6BKZPEHWYO3E3YKREDPQXMS4VK35YLNU7NFBRI26RAN7GI5POFBB' + ) + ]; + let op = StellarBase.Operation.pathPaymentStrictSend({ + sendAsset, + sendAmount, + destination, + destAsset, + destMin, + path, + source + }); + var xdr = op.toXDR('hex'); + var operation = StellarBase.xdr.Operation.fromXDR( + Buffer.from(xdr, 'hex') + ); + var obj = StellarBase.Operation.fromXDRObject(operation); + expect(obj.type).to.be.equal('pathPaymentStrictSend'); + expect(obj.destination).to.be.equal(destination); + expect(obj.source).to.be.equal(source); + }); it('fails to create path payment operation with an invalid destination address', function() { let opts = { destination: 'GCEZW', @@ -1443,7 +1537,22 @@ describe('Operation', function() { expect(obj.type).to.be.equal('accountMerge'); expect(obj.destination).to.be.equal(opts.destination); }); - + it('supports muxed accounts', function() { + var opts = {}; + opts.destination = + 'MAAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITLVL6'; + opts.source = + 'MAAAAAAAAAAAAAB7BQ2L7E5NBWMXDUCMZSIPOBKRDSBYVLMXGSSKF6YNPIB7Y77ITLVL6'; + let op = StellarBase.Operation.accountMerge(opts); + var xdr = op.toXDR('hex'); + var operation = StellarBase.xdr.Operation.fromXDR( + Buffer.from(xdr, 'hex') + ); + var obj = StellarBase.Operation.fromXDRObject(operation); + expect(obj.type).to.be.equal('accountMerge'); + expect(obj.destination).to.be.equal(opts.destination); + expect(obj.source).to.be.equal(opts.source); + }); it('fails to create accountMerge operation with an invalid destination address', function() { let opts = { destination: 'GCEZW'