func (ms *MicroStellar) SignTransaction(b64Tx string, seeds ...string) (string, error) {
	tx := ms.getTx()
	xdrTxe, err := DecodeTx(b64Tx)
	if err != nil {
		return "", ms.wrapf(err, "DecodeTx")
	}
	debugf("SignTransaction", "decoded transaction: %+v", xdrTxe)
	hash, err := network.HashTransaction(&xdrTxe.Tx, tx.network.Passphrase)
	if err != nil {
		return "", ms.wrapf(err, "hash failed")
	}
	for _, seed := range seeds {
		kp, err := keypair.Parse(seed)
		if err != nil {
			return "", ms.wrapf(err, "parse failed")
		}
		sig, err := kp.SignDecorated(hash[:])
		if err != nil {
			return "", ms.wrapf(err, "sign failed")
		}
		debugf("SignTransaction", "adding signature: %+v", sig)
		xdrTxe.Signatures = append(xdrTxe.Signatures, sig)
	}
	signedTx, err := xdr.MarshalBase64(xdrTxe)
	if err != nil {
		return "", ms.wrapf(err, "could not marshal transaction")
	}
	return signedTx, ms.success()
}
SignTransaction signs a base64-encoded transaction envelope with the specified seeds
for the current network.
package microstellar
import (
	"net/http"
	"strings"
	"github.com/pkg/errors"
	"github.com/stellar/go/build"
	"github.com/stellar/go/clients/federation"
	"github.com/stellar/go/clients/horizon"
	"github.com/stellar/go/clients/stellartoml"
	"github.com/stellar/go/keypair"
	"github.com/stellar/go/network"
	"github.com/stellar/go/xdr"
)
type MicroStellar struct {
	networkName string
	params      Params
	fake        bool
	tx          *Tx
	lastTx      *Tx
	lastErr     error
}
type Error struct {
	HorizonError horizon.Error
}
type Params map[string]interface{}
func New(networkName string, params ...Params) *MicroStellar {
	var p Params
	if len(params) > 0 {
		p = params[0]
	}
	return &MicroStellar{
		networkName: networkName,
		params:      p,
		fake:        networkName == "fake",
		tx:          nil,
	}
}
func NewFromSpec(spec string) *MicroStellar {
	parts := strings.SplitN(spec, ";", 3)
	network := ""
	params := Params{}
	if len(parts) > 0 {
		network = parts[0]
		if len(parts) == 3 {
			params = Params{
				"url":        parts[1],
				"passphrase": parts[2],
			}
		}
	} else {
		network = "test"
	}
	return New(network, params)
}
func (ms *MicroStellar) getTx() *Tx {
	var tx *Tx
	if ms.tx != nil {
		tx = ms.tx
	} else {
		tx = NewTx(ms.networkName, ms.params)
	}
	return tx
}
func (ms *MicroStellar) signAndSubmit(tx *Tx, signers ...string) error {
	if !tx.isMultiOp {
		tx.Sign(signers...)
		tx.Submit()
	}
	ms.lastTx = tx
	return ms.err(tx.Err())
}
func (ms *MicroStellar) success() error {
	ms.lastErr = nil
	return ms.lastErr
}
func (ms *MicroStellar) err(err error) error {
	ms.lastErr = err
	return ms.lastErr
}
func (ms *MicroStellar) errorf(msg string, args ...interface{}) error {
	ms.lastErr = errors.Errorf(msg, args...)
	return ms.lastErr
}
func (ms *MicroStellar) wrapf(err error, msg string, args ...interface{}) error {
	ms.lastErr = errors.Wrapf(err, msg, args...)
	return ms.lastErr
}
func (ms *MicroStellar) Err() error {
	return ms.lastErr
}
func (ms *MicroStellar) Response() *TxResponse {
	return ms.lastTx.Response()
}
func (ms *MicroStellar) Start(sourceSeed string, options ...*Options) *MicroStellar {
	ms.tx = NewTx(ms.networkName, ms.params).WithOptions(mergeOptions(options).MultiOp(sourceSeed))
	return ms
}
func (ms *MicroStellar) Submit() error {
	tx := ms.getTx()
	if !tx.isMultiOp {
		return ms.errorf("can't submit, not a multi-op transaction")
	}
	ms.tx.Sign()
	ms.tx.Submit()
	ms.lastTx = tx
	ms.tx = nil
	return ms.err(ms.lastTx.Err())
}
func (ms *MicroStellar) Payload() (string, error) {
	tx := ms.getTx()
	if !tx.isMultiOp {
		return "", ms.errorf("can't generate payload, Start() not called")
	}
	payload, err := tx.Payload()
	ms.lastTx = tx
	ms.tx = nil
	return payload, ms.err(err)
}
func (ms *MicroStellar) CreateKeyPair() (*KeyPair, error) {
	pair, err := keypair.Random()
	if err != nil {
		return nil, ms.err(err)
	}
	debugf("CreateKeyPair", "created address: %s, seed: <redacted>", pair.Address())
	return &KeyPair{pair.Seed(), pair.Address()}, ms.success()
}
func (ms *MicroStellar) FundAccount(sourceSeed string, addressOrSeed string, amount string, options ...*Options) error {
	if !ValidAddressOrSeed(sourceSeed) {
		return ms.errorf("invalid source address or seed: %s", sourceSeed)
	}
	if !ValidAddressOrSeed(addressOrSeed) {
		return ms.errorf("invalid target address or seed: %s", addressOrSeed)
	}
	payment := build.CreateAccount(
		build.Destination{AddressOrSeed: addressOrSeed},
		build.NativeAmount{Amount: amount})
	tx := ms.getTx()
	if len(options) > 0 {
		tx.SetOptions(options[0])
	}
	tx.Build(sourceAccount(sourceSeed), payment)
	return ms.signAndSubmit(tx, sourceSeed)
}
func (ms *MicroStellar) LoadAccount(address string) (*Account, error) {
	if !ValidAddressOrSeed(address) {
		return nil, ms.errorf("can't load account: invalid address or seed: %v", address)
	}
	if ms.fake {
		return newAccount(), ms.success()
	}
	debugf("LoadAccount", "loading account: %s", address)
	tx := NewTx(ms.networkName, ms.params)
	account, err := tx.GetClient().LoadAccount(address)
	if err != nil {
		return nil, ms.wrapf(err, "could not load account")
	}
	return newAccountFromHorizon(account), ms.success()
}
func (ms *MicroStellar) Resolve(address string) (string, error) {
	debugf("Resolve", "looking up: %s", address)
	if !strings.Contains(address, "*") {
		return "", ms.errorf("not a fedaration address: %s", address)
	}
	var fedClient = &federation.Client{
		HTTP:        http.DefaultClient,
		Horizon:     NewTx(ms.networkName, ms.params).GetClient(),
		StellarTOML: stellartoml.DefaultClient,
	}
	resp, err := fedClient.LookupByAddress(address)
	if err != nil {
		return "", ms.wrapf(err, "resolve error")
	}
	return resp.AccountID, ms.success()
}
func (ms *MicroStellar) PayNative(sourceSeed string, targetAddress string, amount string, options ...*Options) error {
	return ms.Pay(sourceSeed, targetAddress, amount, NativeAsset, options...)
}
func (ms *MicroStellar) Pay(sourceAddressOrSeed string, targetAddress string, amount string, asset *Asset, options ...*Options) error {
	if err := asset.Validate(); err != nil {
		return ms.wrapf(err, "can't pay")
	}
	if !ValidAddressOrSeed(sourceAddressOrSeed) {
		return ms.errorf("can't pay: invalid source address or seed: %s", sourceAddressOrSeed)
	}
	if !ValidAddressOrSeed(targetAddress) {
		return ms.errorf("can't pay: invalid address: %v", targetAddress)
	}
	paymentMuts := []interface{}{
		build.Destination{AddressOrSeed: targetAddress},
	}
	if asset.IsNative() {
		paymentMuts = append(paymentMuts, build.NativeAmount{Amount: amount})
	} else {
		paymentMuts = append(paymentMuts,
			build.CreditAmount{Code: asset.Code, Issuer: asset.Issuer, Amount: amount})
	}
	tx := ms.getTx()
	if len(options) > 0 {
		opts := options[0]
		tx.SetOptions(opts)
		if opts.sendAsset != nil {
			debugf("Pay", "path payment: deposit %s with %s", asset.Code, opts.sendAsset.Code)
			payPath := build.PayWith(opts.sendAsset.ToStellarAsset(), opts.maxAmount)
			if len(opts.path) > 0 {
				for _, through := range opts.path {
					debugf("Pay", "path payment: through %s", through.Code)
					payPath = payPath.Through(through.ToStellarAsset())
				}
			} else {
				debugf("Pay", "no path specified, searching for paths from: %s", opts.sourceAddress)
				if err := ValidAddress(opts.sourceAddress); err != nil {
					return ms.wrapf(err, "not a valid source address: %s", opts.sourceAddress)
				}
				paths, err := ms.FindPaths(opts.sourceAddress, targetAddress, asset, amount, Opts().WithAsset(opts.sendAsset, opts.maxAmount))
				if err != nil {
					return ms.wrapf(err, "path finding error")
				}
				if len(paths) < 1 {
					return ms.errorf("no paths found from %s to %s", opts.sendAsset.Code, asset.Code)
				}
				for _, hop := range paths[0].Hops {
					debugf("Pay", "path payment: through %s", hop.Code)
					payPath = payPath.Through(hop.ToStellarAsset())
				}
			}
			paymentMuts = append(paymentMuts, payPath)
		}
	}
	tx.Build(sourceAccount(sourceAddressOrSeed), build.Payment(paymentMuts...))
	return ms.signAndSubmit(tx, sourceAddressOrSeed)
}
func (ms *MicroStellar) CreateTrustLine(sourceSeed string, asset *Asset, limit string, options ...*Options) error {
	if !ValidAddressOrSeed(sourceSeed) {
		return ms.errorf("can't create trust line: invalid source address or seed: %s", sourceSeed)
	}
	if err := asset.Validate(); err != nil {
		return ms.wrapf(err, "can't create trust line")
	}
	tx := ms.getTx()
	if len(options) > 0 {
		tx.SetOptions(options[0])
	}
	if limit == "" {
		tx.Build(sourceAccount(sourceSeed), build.Trust(asset.Code, asset.Issuer))
	} else {
		tx.Build(sourceAccount(sourceSeed), build.Trust(asset.Code, asset.Issuer, build.Limit(limit)))
	}
	return ms.signAndSubmit(tx, sourceSeed)
}
func (ms *MicroStellar) RemoveTrustLine(sourceSeed string, asset *Asset, options ...*Options) error {
	if !ValidAddressOrSeed(sourceSeed) {
		return ms.errorf("can't remove trust line: invalid source address or seed: %s", sourceSeed)
	}
	if err := asset.Validate(); err != nil {
		return ms.wrapf(err, "can't remove trust line")
	}
	tx := ms.getTx()
	if len(options) > 0 {
		tx.SetOptions(options[0])
	}
	tx.Build(sourceAccount(sourceSeed), build.RemoveTrust(asset.Code, asset.Issuer))
	return ms.signAndSubmit(tx, sourceSeed)
}
func (ms *MicroStellar) AllowTrust(sourceSeed string, address string, assetCode string, authorized bool, options ...*Options) error {
	if !ValidAddressOrSeed(sourceSeed) {
		return ms.errorf("can't authorize trust line: invalid source address or seed: %s", sourceSeed)
	}
	if err := ValidAddress(address); err != nil {
		return ms.errorf("can't authorize trust line: invalid account address: %s", address)
	}
	tx := ms.getTx()
	if len(options) > 0 {
		tx.SetOptions(options[0])
	}
	tx.Build(sourceAccount(sourceSeed), build.AllowTrust(
		build.Trustor{Address: address},
		build.AllowTrustAsset{Code: assetCode},
		build.Authorize{Value: authorized}))
	return ms.signAndSubmit(tx, sourceSeed)
}
func (ms *MicroStellar) SetMasterWeight(sourceSeed string, weight uint32, options ...*Options) error {
	if !ValidAddressOrSeed(sourceSeed) {
		return ms.errorf("can't set master weight: invalid source address or seed: %s", sourceSeed)
	}
	tx := ms.getTx()
	if len(options) > 0 {
		tx.SetOptions(options[0])
	}
	tx.Build(sourceAccount(sourceSeed), build.MasterWeight(weight))
	return ms.signAndSubmit(tx, sourceSeed)
}
type AccountFlags int32
const (
	FlagsNone = AccountFlags(0)
	FlagAuthRequired = AccountFlags(1)
	FlagAuthRevocable = AccountFlags(2)
	FlagAuthImmutable = AccountFlags(4)
)
func (ms *MicroStellar) SetFlags(sourceSeed string, flags AccountFlags, options ...*Options) error {
	if !ValidAddressOrSeed(sourceSeed) {
		return ms.errorf("can't set flags: invalid source address or seed: %s", sourceSeed)
	}
	tx := ms.getTx()
	if len(options) > 0 {
		tx.SetOptions(options[0])
	}
	tx.Build(sourceAccount(sourceSeed), build.SetFlag(int32(flags)))
	return ms.signAndSubmit(tx, sourceSeed)
}
func (ms *MicroStellar) ClearFlags(sourceSeed string, flags AccountFlags, options ...*Options) error {
	if !ValidAddressOrSeed(sourceSeed) {
		return ms.errorf("can't clear flags: invalid source address or seed: %s", sourceSeed)
	}
	tx := ms.getTx()
	if len(options) > 0 {
		tx.SetOptions(options[0])
	}
	tx.Build(sourceAccount(sourceSeed), build.ClearFlag(int32(flags)))
	return ms.signAndSubmit(tx, sourceSeed)
}
func (ms *MicroStellar) SetHomeDomain(sourceSeed string, domain string, options ...*Options) error {
	if !ValidAddressOrSeed(sourceSeed) {
		return ms.errorf("can't set home domain: invalid source address or seed: %s", sourceSeed)
	}
	tx := ms.getTx()
	if len(options) > 0 {
		tx.SetOptions(options[0])
	}
	tx.Build(sourceAccount(sourceSeed), build.HomeDomain(domain))
	return ms.signAndSubmit(tx, sourceSeed)
}
func (ms *MicroStellar) AddSigner(sourceSeed string, signerAddress string, signerWeight uint32, options ...*Options) error {
	if !ValidAddressOrSeed(sourceSeed) {
		return ms.errorf("can't add signer: invalid source address or seed: %s", sourceSeed)
	}
	if !ValidAddressOrSeed(signerAddress) {
		return ms.errorf("can't add signer: invalid signer address or seed: %s", signerAddress)
	}
	tx := ms.getTx()
	if len(options) > 0 {
		tx.SetOptions(options[0])
	}
	tx.Build(sourceAccount(sourceSeed), build.AddSigner(signerAddress, signerWeight))
	return ms.signAndSubmit(tx, sourceSeed)
}
func (ms *MicroStellar) RemoveSigner(sourceSeed string, signerAddress string, options ...*Options) error {
	if !ValidAddressOrSeed(sourceSeed) {
		return ms.errorf("can't remove signer: invalid source address or seed: %s", sourceSeed)
	}
	if !ValidAddressOrSeed(signerAddress) {
		return ms.errorf("can't remove signer: invalid signer address or seed: %s", signerAddress)
	}
	tx := ms.getTx()
	if len(options) > 0 {
		tx.SetOptions(options[0])
	}
	tx.Build(sourceAccount(sourceSeed), build.RemoveSigner(signerAddress))
	return ms.signAndSubmit(tx, sourceSeed)
}
func (ms *MicroStellar) SetThresholds(sourceSeed string, low, medium, high uint32, options ...*Options) error {
	if !ValidAddressOrSeed(sourceSeed) {
		return ms.errorf("can't set thresholds: invalid source address or seed: %s", sourceSeed)
	}
	tx := ms.getTx()
	if len(options) > 0 {
		tx.SetOptions(options[0])
	}
	tx.Build(sourceAccount(sourceSeed), build.SetThresholds(low, medium, high))
	return ms.signAndSubmit(tx, sourceSeed)
}
func (ms *MicroStellar) SetData(sourceSeed string, key string, val []byte, options ...*Options) error {
	if !ValidAddressOrSeed(sourceSeed) {
		return ms.errorf("can't set thresholds: invalid source address or seed: %s", sourceSeed)
	}
	tx := ms.getTx()
	if len(options) > 0 {
		tx.SetOptions(options[0])
	}
	if key == "" {
		return ms.errorf("data key must not be empty")
	}
	if len(key) > 64 {
		return ms.errorf("data key must be under 64 bytes: %s", key)
	}
	if len(val) > 64 {
		return ms.errorf("data value must be under 64 bytes: %s", string(val))
	}
	tx.Build(sourceAccount(sourceSeed), build.SetData(key, val))
	return ms.signAndSubmit(tx, sourceSeed)
}
func (ms *MicroStellar) ClearData(sourceSeed string, key string, options ...*Options) error {
	if !ValidAddressOrSeed(sourceSeed) {
		return ms.errorf("can't set thresholds: invalid source address or seed: %s", sourceSeed)
	}
	tx := ms.getTx()
	if len(options) > 0 {
		tx.SetOptions(options[0])
	}
	if len(key) > 64 {
		return ms.errorf("data key must be under 64 bytes: %s", key)
	}
	tx.Build(sourceAccount(sourceSeed), build.ClearData(key))
	return ms.signAndSubmit(tx, sourceSeed)
}