executor

package
v0.0.11 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 27, 2022 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Index

Constants

View Source
const DENOMINATOR = 10000
View Source
const MAX_UINT256 = "115792089237316195423570985008687907853269984665640564039457584007913129639935"

Variables

View Source
var (
	ZeroEvents               = errors.New("Got zero event logs, expected at least one")
	StatusCodeZero           = errors.New("Got status code zero(failed)")
	ExternalContextCancelled = errors.New("External Context Cancelled")
	NilEventReceived         = errors.New("Nil Event Received")
	InsufficientNativeToken  = errors.New("Insufficient Native Token")
	InsufficientWrappedCoin  = errors.New("Insufficient Wrapped Coin")
	InsufficientUnknownCoin  = errors.New("Insufficient Unknown Coin")
	UnsupportedCoinArgs      = errors.New("Unsupported Coin Args")
	IgnoreableError          = errors.New("Ignoreable Error")
)
View Source
var (
	APICallerFunc = map[chain.ChainType]NewApiCaller{}
)

Functions

func New

func New(l log.Logger, cfg *Config) (ex *executor, err error)

func SetLogger

func SetLogger(cfg *Config) log.Logger

Types

type Config

type Config struct {
	config.FileConfig          `json:",squash"`
	LogLevel                   string            `json:"log_level"`
	ConsoleLevel               string            `json:"console_level"`
	LogWriter                  *log.WriterConfig `json:"log_writer,omitempty"`
	EnableExperimentalFeatures bool              `json:"enable_experimental_features"`
	Chains                     []*chain.Config   `json:"chains"`
	FeeAggregatorAddress       string            `json:"fee_aggregator"`
}

func LoadConfig

func LoadConfig(file string) (*Config, error)

type ConfigureScript

type ConfigureScript struct {
	Name        string
	Type        string
	Description string
	Callback    configureCallBack
}
var ConfigureFeeChange ConfigureScript = ConfigureScript{
	Name:        "ChangeFee",
	Description: "Change Fee",
	Type:        "Configure",
	Callback: func(ctx context.Context, conf *configPoint, ts *testSuite) (txnRec *txnRecord, errs error) {
		if len(conf.Fee) == 0 {
			errs = IgnoreableError
			return
		}
		stdCfg, err := ts.GetStandardConfigAPI(conf.chainName)
		if err != nil {
			errs = errors.Wrapf(err, "GetStandardConfigAPI %v", err)
			ts.logger.Error(errs)
			return
		}
		for coinName, fee := range conf.Fee {
			setFeeHash, err := stdCfg.SetFeeRatio(ts.GetStandardConfigAPIOwnerKey(conf.chainName), coinName, fee[0], fee[1])
			if err != nil {
				errs = errors.Wrapf(err, "SetFeeRatio %v", err)
				ts.logger.Error(errs)
				return
			}
			if _, errs = ts.ValidateTransactionResult(ctx, conf.chainName, setFeeHash); errs != nil {
				ts.logger.Error("ValidateTransactionResult SetFeeRatio Hash %v Err %v", setFeeHash, errs)
				return
			}
		}
		for coinName, fee := range conf.Fee {
			newFeeNumerator, newFixedFee, err := stdCfg.GetFeeRatio(coinName)
			if err != nil {
				errs = errors.Wrapf(err, "GetFeeRatio %v", err)
				ts.logger.Error(errs)
				return
			}
			if newFeeNumerator.Cmp(fee[0]) != 0 {
				errs = fmt.Errorf("Expected same. Got newFeeNumerator %v feeNumerator %v", newFeeNumerator, fee[0])
				ts.logger.Error(errs)
				return
			}
			if newFixedFee.Cmp(fee[1]) != 0 {
				errs = fmt.Errorf("Expected same. Got newFeeNumerator %v feeNumerator %v", newFixedFee, fee[1])
				ts.logger.Error(errs)
				return
			}
		}
		return
	},
}
var ConfigureTokenLimit ConfigureScript = ConfigureScript{
	Name:        "ConfigureTokenLimit",
	Description: "Configure Token Limit",
	Type:        "Configure",
	Callback: func(ctx context.Context, conf *configPoint, ts *testSuite) (txnRec *txnRecord, errs error) {
		txnRec = &txnRecord{
			feeRecords: []*feeRecord{},
			addresses:  make(map[chain.ChainType][]keypair),
		}
		if len(conf.TokenLimits) == 0 {
			errs = IgnoreableError
			return
		}
		fCfg, err := ts.GetFullConfigAPI()
		if err != nil {
			errs = errors.Wrapf(err, "GetFullConfigAPI %v", err)
			ts.logger.Error(errs)
			return
		}
		coinNames := []string{}
		tokenLimits := []*big.Int{}
		for k, v := range conf.TokenLimits {
			coinNames = append(coinNames, k)
			tokenLimits = append(tokenLimits, v)
		}
		setTokenHash, err := fCfg.SetTokenLimit(ts.FullConfigAPIsOwner(), coinNames, tokenLimits)
		if err != nil {
			errs = errors.Wrapf(err, "setTokenHash %v", err)
			ts.logger.Error(errs)
			return
		}
		if _, err := ts.ValidateTransactionResult(ctx, ts.FullConfigAPIChain(), setTokenHash); err != nil {
			errs = errors.Wrapf(err, "ValidateTransactionResult %v", err)
			ts.logger.Error(errs)
			return
		}
		responseChains := ts.StdConfigAPIChains()
		if len(responseChains) == 0 {
			errs = fmt.Errorf("Expected finite standard ConfigAPI chains.Got zero")
		}

		err = ts.WaitForConfigResponse(ctx, chain.TokenLimitRequest, chain.TokenLimitResponse, responseChains[0], setTokenHash,
			map[chain.EventLogType]func(event *evt) error{
				chain.TokenLimitRequest: func(ev *evt) error {
					if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
						return errors.New("Got nil value for event ")
					}
					reqEvt, ok := ev.msg.EventLog.(*chain.TokenLimitRequestEvent)
					if !ok {
						return fmt.Errorf("Expected *chain.TokenLimitRequestEvent. Got %T", ev.msg.EventLog)
					}
					txnRec.feeRecords = append(txnRec.feeRecords, &feeRecord{
						ChainName: ts.FullConfigAPIChain(),
						Sn:        reqEvt.Sn,
						Fee:       map[string]*big.Int{},
					})
					if len(reqEvt.CoinNames) != len(conf.TokenLimits) && len(reqEvt.TokenLimits) != len(conf.TokenLimits) {
						return fmt.Errorf("Expected same len reqEvt.CoinNames %v reqEvt.TokenLimits %v conf.TokenLimits %v", len(reqEvt.CoinNames), len(reqEvt.TokenLimits), len(conf.TokenLimits))
					}
					for i := 0; i < len(reqEvt.CoinNames); i++ {
						if confTokenLimit, ok := conf.TokenLimits[reqEvt.CoinNames[i]]; !ok {
							return fmt.Errorf("Unexpected coinName reqEvt.CoinName %v", reqEvt.CoinNames[i])
						} else if ok && confTokenLimit.Cmp(reqEvt.TokenLimits[i]) != 0 {
							return fmt.Errorf("Expected same. Got Different reqEvt.TokenLimit %v conf.TokenLimit %v", reqEvt.TokenLimits[i], confTokenLimit)
						}
					}
					return nil
				},
				chain.TokenLimitResponse: func(ev *evt) error {
					if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
						return errors.New("Got nil value for event ")
					}
					resEvt, ok := ev.msg.EventLog.(*chain.TokenLimitResponseEvent)
					if !ok {
						return fmt.Errorf("Expected *chain.TokenLimitResponseEvent. Got %T", ev.msg.EventLog)
					}

					if resEvt.Code != 0 {
						return fmt.Errorf("Expected Code 0; Got Sn %v Code %v Msg %v", resEvt.Sn, resEvt.Code, resEvt.Msg)
					}
					return nil
				},
			},
		)
		if err != nil {
			errs = errors.Wrapf(err, "WaitForConfigResponse %v", err)
			ts.logger.Error(errs)
			return
		}

		for k, v := range conf.TokenLimits {
			if nv, err := fCfg.GetTokenLimit(k); err != nil {
				errs = errors.Wrapf(err, "GetTokenLimit %v", err)
				ts.logger.Error(errs)
				return
			} else if err == nil && v.Cmp(nv) != 0 {
				errs = errors.Wrapf(err, "Expected same token limit. Got different input %v output %v ", v, nv)
				return
			}
		}
		return
	},
}

type NewApiCaller

type NewApiCaller func(l log.Logger, cfg *chain.Config) (chain.ChainAPI, error)

type Script

type Script struct {
	Name        string
	Type        string
	Description string
	Callback    callBackFunc
}
var TransferBatchBiDirection Script = Script{
	Name:        "TransferBatchBiDirection",
	Description: "Transfer batch bi-direction",
	Type:        "Transfer",
	Callback: func(ctx context.Context, tp *transferPoint, ts *testSuite) (txnRec *txnRecord, errs error) {
		srcChain := tp.SrcChain
		dstChain := tp.DstChain
		coinNames := tp.CoinNames

		txnRec = &txnRecord{
			feeRecords: []*feeRecord{},
			addresses:  make(map[chain.ChainType][]keypair),
		}

		if len(coinNames) == 0 {
			errs = UnsupportedCoinArgs
			ts.logger.Debug(fmt.Errorf(" Should specify at least one coinName, got zero"))
			return
		}
		src, tmpOk := ts.clsPerChain[srcChain]
		if !tmpOk {
			errs = fmt.Errorf("Chain %v not found", srcChain)
			ts.logger.Error(errs)
			return
		}
		dst, tmpOk := ts.clsPerChain[dstChain]
		if !tmpOk {
			errs = fmt.Errorf("Chain %v not found", srcChain)
			ts.logger.Error(errs)
			return
		}
		if len(coinNames) == 1 && coinNames[0] == src.NativeCoin() {
			errs = UnsupportedCoinArgs
			ts.logger.Debug(fmt.Errorf("A single src.NativeCoin %v has been used", coinNames[0]))
			return
		}
		if len(coinNames) == 1 && coinNames[0] == dst.NativeCoin() {
			errs = UnsupportedCoinArgs
			ts.logger.Debug(fmt.Errorf("A single dst.NativeCoin %v has been used", coinNames[0]))
			return
		}
		srcKey, srcAddr, err := ts.GetKeyPairs(srcChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetKeyPairs %v", err)
			ts.logger.Error(errs)
			return
		}
		txnRec.addresses[srcChain] = []keypair{{PrivKey: srcKey, PubKey: srcAddr}}
		dstKey, dstAddr, err := ts.GetKeyPairs(dstChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetKeyPairs %v", err)
			ts.logger.Error(errs)
			return
		}
		txnRec.addresses[dstChain] = []keypair{{PrivKey: dstKey, PubKey: dstAddr}}
		ts.logger.Debug("Calculate Transfer Amounts")

		tokenAmountAfterFeeChargeOnDst := big.NewInt(1)
		tokenAmountBeforeFeeChargeOnDst := make([]*big.Int, len(coinNames))
		nativeCoinAmountBeforeFeeChargeOnDst := big.NewInt(0)
		for i := 0; i < len(coinNames); i++ {
			tokenAmountBeforeFeeChargeOnDst[i], err = ts.getAmountBeforeFeeCharge(dstChain, coinNames[i], tokenAmountAfterFeeChargeOnDst)
			if err != nil {
				errs = errors.Wrapf(err, "getAmountBeforeFeeCharge(%v) %v", coinNames[i], err)
				ts.logger.Error(errs)
				return
			}
			if coinNames[i] == dst.NativeCoin() {
				nativeCoinAmountBeforeFeeChargeOnDst.Set(tokenAmountBeforeFeeChargeOnDst[i])
			}
		}
		tokenAmountBeforeFeeChargeOnSrc := make([]*big.Int, len(coinNames))
		nativeCoinAmountBeforeFeeChargeOnSrc := big.NewInt(0)
		for i := 0; i < len(coinNames); i++ {
			tokenAmountBeforeFeeChargeOnSrc[i], err = ts.getAmountBeforeFeeCharge(dstChain, coinNames[i], tokenAmountBeforeFeeChargeOnDst[i])
			if err != nil {
				errs = errors.Wrapf(err, "getAmountBeforeFeeCharge(%v) %v", coinNames[i], err)
				ts.logger.Error(errs)
				return
			}
			if coinNames[i] == src.NativeCoin() {
				nativeCoinAmountBeforeFeeChargeOnSrc.Set(tokenAmountBeforeFeeChargeOnSrc[i])
			}
		}
		ts.logger.Debug("Calculate Gas Fees")

		gasLimitOnSrc := big.NewInt(int64(ts.cfgPerChain[srcChain].GasLimit[chain.TransferBatchCoinInterChainGasLimit]))
		for _, coinName := range coinNames {
			if coinName == src.NativeCoin() {
				continue
			}
			gasLimitOnSrc.Add(gasLimitOnSrc, big.NewInt(int64(ts.cfgPerChain[srcChain].GasLimit[chain.ApproveTokenInterChainGasLimit])))
		}
		gasFeeOnSrc := (&big.Int{}).Mul(src.SuggestGasPrice(), gasLimitOnSrc)
		gasLimitOnDst := big.NewInt(int64(ts.cfgPerChain[dstChain].GasLimit[chain.TransferBatchCoinInterChainGasLimit]))
		for _, coinName := range coinNames {
			if coinName == src.NativeCoin() {
				continue
			}
			gasLimitOnDst.Add(gasLimitOnDst, big.NewInt(int64(ts.cfgPerChain[dstChain].GasLimit[chain.ApproveTokenInterChainGasLimit])))
		}

		gasFeeOnDst := (&big.Int{}).Mul(dst.SuggestGasPrice(), gasLimitOnDst)

		ts.logger.Debug("Fund")

		for i := 0; i < len(coinNames); i++ {
			if errs = ts.Fund(srcChain, srcAddr, tokenAmountBeforeFeeChargeOnSrc[i], coinNames[i]); errs != nil {

				ts.logger.Debug(errors.Wrapf(err, "srcChain %v, srcAddr %v, coinName %v err %v", srcChain, srcAddr, coinNames[i], err))
				return
			}
		}
		if errs = ts.Fund(srcChain, srcAddr, gasFeeOnSrc, src.NativeCoin()); errs != nil {

			ts.logger.Debug(errors.Wrapf(err, "srcChain %v, srcAddr %v, coinName %v err %v", srcChain, srcAddr, src.NativeCoin(), err))
			return
		}
		if errs = ts.Fund(dstChain, dstAddr, gasFeeOnDst, dst.NativeCoin()); errs != nil {

			ts.logger.Debug(errors.Wrapf(err, "srcChain %v, srcAddr %v, coinName %v err %v", srcChain, srcAddr, dst.NativeCoin(), err))
			return
		}

		ts.logger.Debug("Record Initial Balance")

		initSrcBalance := make([]*chain.CoinBalance, len(coinNames))
		initDstBalance := make([]*chain.CoinBalance, len(coinNames))
		for i := 0; i < len(coinNames); i++ {
			initSrcBalance[i], err = src.GetCoinBalance(coinNames[i], srcAddr)
			if err != nil {
				errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
				ts.logger.Error(errs)
				return
			}
			initDstBalance[i], err = dst.GetCoinBalance(coinNames[i], dstAddr)
			if err != nil {
				errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
				ts.logger.Error(errs)
				return
			}
		}
		initSrcNativeCoinBalance, err := src.GetCoinBalance(src.NativeCoin(), srcAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		initDstNativeCoinBalance, err := dst.GetCoinBalance(dst.NativeCoin(), dstAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}

		ts.logger.Debug("ApproveToken On Source")

		approveHash := map[string]string{}
		for i, coinName := range coinNames {
			if coinName != src.NativeCoin() {
				if approveHash[coinName], err = src.Approve(coinName, srcKey, tokenAmountBeforeFeeChargeOnSrc[i]); err != nil {
					errs = errors.Wrapf(err, "Approve Err: %v Hash %v", err, approveHash[coinName])
					ts.logger.Error(errs)
					return
				} else {
					if _, err := ts.ValidateTransactionResult(ctx, srcChain, approveHash[coinName]); err != nil {
						errs = errors.Wrapf(err, "Approve ValidateTransactionResult Err: %v Hash %v", err, approveHash[coinName])
						ts.logger.Error(errs)
						return
					}
				}
			}
		}

		ts.logger.Debug("Transfer On Source")

		transferHashOnSrc, err := src.TransferBatch(coinNames, srcKey, dstAddr, tokenAmountBeforeFeeChargeOnSrc)
		if err != nil {
			errs = errors.Wrapf(err, "Transfer Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		ts.logger.Debug("transferHashOnSrc ", transferHashOnSrc)
		if _, err := ts.ValidateTransactionResult(ctx, srcChain, transferHashOnSrc); err != nil {
			errs = errors.Wrapf(err, "ValidateTransactionResult %v", err)
			ts.logger.Error(errs)
			return
		}

		ts.logger.Debug("Wait For Events")

		err = ts.WaitForTransferEvents(ctx, srcChain, dstChain, transferHashOnSrc, map[chain.EventLogType]func(*evt) error{
			chain.TransferStart: func(ev *evt) error {
				if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
					return errors.New("Got nil value for event ")
				}
				startEvt, ok := ev.msg.EventLog.(*chain.TransferStartEvent)
				if !ok {
					return fmt.Errorf("Expected *chain.TransferStartEvent. Got %T", ev.msg.EventLog)
				}
				txnRec.feeRecords = append(txnRec.feeRecords, &feeRecord{
					ChainName: srcChain,
					Sn:        startEvt.Sn,
					Fee:       map[string]*big.Int{},
				})
				for i := 0; i < len(startEvt.Assets); i++ {
					txnRec.feeRecords[len(txnRec.feeRecords)-1].Fee[startEvt.Assets[i].Name] = startEvt.Assets[i].Fee
				}

				startEvt.From = src.GetBTPAddress(startEvt.From)
				if startEvt.From != srcAddr {
					return fmt.Errorf("Expected Same Value for SrcAddr; Got startEvt.From: %v srcAddr: %v", startEvt.From, srcAddr)
				}
				if startEvt.To != dstAddr {
					return fmt.Errorf("Expected Same Value for DstAddr; Got startEvt.To: %v dstAddr: %v", startEvt.To, dstAddr)
				}
				if len(startEvt.Assets) != len(coinNames) {
					return fmt.Errorf("For single token transfer; Expected single asset; Got %v", len(startEvt.Assets))
				}
				for _, assets := range startEvt.Assets {
					index := -1
					for i, coinName := range coinNames {
						if coinName == assets.Name {
							index = i
							break
						}
					}
					if index == -1 {
						return fmt.Errorf("Asset name %v not on coinNames list", assets.Name)
					}

					if assets.Value.Cmp(tokenAmountBeforeFeeChargeOnDst[index]) != 0 {
						return fmt.Errorf("Expected same value for coinName; Got startEvt.Value: %v AmountAfterFeeCharge %v", assets.Value, tokenAmountBeforeFeeChargeOnDst[index])
					}
					sum := (&big.Int{}).Add(assets.Value, assets.Fee)
					if sum.Cmp(tokenAmountBeforeFeeChargeOnSrc[index]) != 0 {
						return fmt.Errorf("Expected same value for coinName; Got startEvt.Value+Fee: %v AmountBeforeFeeCharge %v", sum, tokenAmountBeforeFeeChargeOnSrc[index])
					}
				}
				return nil
			},
			chain.TransferReceived: func(ev *evt) error {
				if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
					return errors.New("Got nil value for event ")
				}
				receivedEvt, ok := ev.msg.EventLog.(*chain.TransferReceivedEvent)
				if !ok {
					return fmt.Errorf("Expected *chain.TransferReceivedEvent. Got %T", ev.msg.EventLog)
				}
				receivedEvt.To = dst.GetBTPAddress(receivedEvt.To)
				if receivedEvt.To != dstAddr {
					return fmt.Errorf("Expected Same Value for DstAddr; Got receivedEvt.To: %v dstAddr: %v", receivedEvt.To, dstAddr)
				}
				if len(receivedEvt.Assets) != len(coinNames) {
					return fmt.Errorf("For single token transfer; Expected single asset; Got %v", len(receivedEvt.Assets))
				}
				for _, assets := range receivedEvt.Assets {
					index := -1
					for i, coinName := range coinNames {
						if coinName == assets.Name {
							index = i
							break
						}
					}
					if index == -1 {
						return fmt.Errorf("Asset name %v not on coinNames list", assets.Name)
					}
					if assets.Value.Cmp(tokenAmountBeforeFeeChargeOnDst[index]) != 0 {
						return fmt.Errorf("Expected same value for coinName; Got receivedEvt.Value: %v AmountAfterFeeCharge %v", assets.Value, tokenAmountBeforeFeeChargeOnDst[index])
					}
				}
				return nil
			},
			chain.TransferEnd: func(ev *evt) error {
				if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
					return errors.New("Got nil value for event ")
				}
				endEvt, ok := ev.msg.EventLog.(*chain.TransferEndEvent)
				if !ok {
					return fmt.Errorf("Expected *chain.TransferEndEvent. Got %T", ev.msg.EventLog)
				}

				if endEvt.Code.String() == "0" {
					ts.logger.Debug("Got Transfer End")
					return nil
				}
				return fmt.Errorf("Unexpected code %v and response %v", endEvt.Code, endEvt.Response)
			},
		})
		if err != nil {
			errs = errors.Wrapf(err, "WaitForTransferEvents %v", err)
			ts.logger.Error(errs)
			return
		}
		ts.logger.Debug("Intermediate Tally")

		intermediateSrcBalance := make([]*chain.CoinBalance, len(coinNames))
		intermediateDstBalance := make([]*chain.CoinBalance, len(coinNames))
		for i, coinName := range coinNames {
			intermediateSrcBalance[i], err = src.GetCoinBalance(coinName, srcAddr)
			if err != nil {
				errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
				ts.logger.Error(errs)
				return
			}
			intermediateDstBalance[i], err = dst.GetCoinBalance(coinName, dstAddr)
			if err != nil {
				errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
				ts.logger.Error(errs)
				return
			}
		}
		intermediateSrcNativeCoinBalance, err := src.GetCoinBalance(src.NativeCoin(), srcAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}

		gasSpentOnTxn, err := src.ChargedGasFee(transferHashOnSrc)
		if err != nil {
			errs = errors.Wrapf(err, "ChargedGasFee For Transfer Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		for _, coinName := range coinNames {
			if coinName != src.NativeCoin() {
				gasSpentOnApprove, err := src.ChargedGasFee(approveHash[coinName])
				if err != nil {
					errs = errors.Wrapf(err, "GetGasUsed for Approve Err: %v", err)
					ts.logger.Error(errs)
					return
				}
				gasSpentOnTxn.Add(gasSpentOnTxn, gasSpentOnApprove)
			}
		}
		for i, coinName := range coinNames {
			if coinName != src.NativeCoin() {
				tmpDiff := (&big.Int{}).Sub(initSrcBalance[i].UserBalance, intermediateSrcBalance[i].UserBalance)
				if tokenAmountBeforeFeeChargeOnSrc[i].Cmp(tmpDiff) != 0 {
					errs = fmt.Errorf("Expected same value for src balance After transfer, Got TransferAmt %v SrcDiffAmt %v", tokenAmountBeforeFeeChargeOnSrc[i], tmpDiff)
					ts.logger.Error(errs)
					return
				}
				tmpDiff = (&big.Int{}).Sub(initSrcNativeCoinBalance.UserBalance, intermediateSrcNativeCoinBalance.UserBalance)
				tmpNativeCoinUsed := (&big.Int{}).Add(nativeCoinAmountBeforeFeeChargeOnSrc, gasSpentOnTxn)
				if tmpNativeCoinUsed.Cmp(tmpDiff) != 0 {
					errs = fmt.Errorf("Expected same value for src nativeCoin balance after txn; Got GasSpentOnTxn %v srcDiffAmt %v", gasSpentOnTxn, tmpDiff)
					ts.logger.Error(errs)
					return
				}
			} else {
				tmpNativeCoinUsed := (&big.Int{}).Add(tokenAmountBeforeFeeChargeOnSrc[i], gasSpentOnTxn)
				tmpDiff := (&big.Int{}).Sub(initSrcBalance[i].UserBalance, intermediateSrcBalance[i].UserBalance)
				if tmpNativeCoinUsed.Cmp(tmpDiff) != 0 {
					errs = fmt.Errorf("Expected same value for src balance After transfer, Got TransferAmt %v SrcDiffAmt %v", tmpNativeCoinUsed, tmpDiff)
					ts.logger.Error(errs)
					return
				}
			}
			tmpDiff := (&big.Int{}).Sub(intermediateDstBalance[i].UserBalance, initDstBalance[i].UserBalance)
			if tokenAmountBeforeFeeChargeOnDst[i].Cmp(tmpDiff) != 0 {
				errs = fmt.Errorf("Expected same value for dst balance After transfer, Got TransferAmt %v DstDiffAmt %v", tokenAmountBeforeFeeChargeOnDst[i], tmpDiff)
				ts.logger.Error(errs)
				return
			}
		}

		ts.logger.Debug("ApproveToken On Destination")

		approveHash = map[string]string{}
		for i, coinName := range coinNames {
			if coinName != dst.NativeCoin() {
				if approveHash[coinName], err = dst.Approve(coinName, dstKey, tokenAmountBeforeFeeChargeOnDst[i]); err != nil {
					errs = errors.Wrapf(err, "Approve Err: %v Hash %v", err, approveHash[coinName])
					ts.logger.Error(errs)
					return
				} else {
					if _, err := ts.ValidateTransactionResult(ctx, dstChain, approveHash[coinName]); err != nil {
						errs = errors.Wrapf(err, "Approve ValidateTransactionResult Err: %v Hash %v", err, approveHash[coinName])
						ts.logger.Error(errs)
						return
					}
				}
			}
		}

		ts.logger.Debug("Transfer On Destination")

		transferHashOnDst, err := dst.TransferBatch(coinNames, dstKey, srcAddr, tokenAmountBeforeFeeChargeOnDst)
		if err != nil {
			errs = errors.Wrapf(err, "Transfer Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		ts.logger.Debug("transferHashOnDst ", transferHashOnDst)
		if _, err := ts.ValidateTransactionResult(ctx, dstChain, transferHashOnDst); err != nil {
			errs = errors.Wrapf(err, "ValidateTransactionResult %v", err)
			ts.logger.Error(errs)
			return
		}

		ts.logger.Debug("Wait For Events")

		err = ts.WaitForTransferEvents(ctx, dstChain, srcChain, transferHashOnDst, map[chain.EventLogType]func(*evt) error{
			chain.TransferStart: func(ev *evt) error {
				if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
					return errors.New("Got nil value for event ")
				}
				startEvt, ok := ev.msg.EventLog.(*chain.TransferStartEvent)
				if !ok {
					return fmt.Errorf("Expected *chain.TransferStartEvent. Got %T", ev.msg.EventLog)
				}
				txnRec.feeRecords = append(txnRec.feeRecords, &feeRecord{
					ChainName: dstChain,
					Sn:        startEvt.Sn,
					Fee:       map[string]*big.Int{},
				})

				for i := 0; i < len(startEvt.Assets); i++ {
					txnRec.feeRecords[len(txnRec.feeRecords)-1].Fee[startEvt.Assets[i].Name] = startEvt.Assets[i].Fee
				}
				startEvt.From = dst.GetBTPAddress(startEvt.From)
				if startEvt.From != dstAddr {
					return fmt.Errorf("Expected Same Value for SrcAddr; Got startEvt.From: %v srcAddr: %v", startEvt.From, dstAddr)
				}
				if startEvt.To != srcAddr {
					return fmt.Errorf("Expected Same Value for DstAddr; Got startEvt.To: %v dstAddr: %v", startEvt.To, srcAddr)
				}
				if len(startEvt.Assets) != len(coinNames) {
					return fmt.Errorf("For single token transfer; Expected single asset; Got %v", len(startEvt.Assets))
				}
				for _, assets := range startEvt.Assets {
					index := -1
					for i, coinName := range coinNames {
						if coinName == assets.Name {
							index = i
							break
						}
					}
					if index == -1 {
						return fmt.Errorf("Asset name %v not on coinNames list", assets.Name)
					}
					if assets.Value.Cmp(tokenAmountAfterFeeChargeOnDst) != 0 {
						return fmt.Errorf("Expected same value for coinName; Got startEvt.Value: %v AmountAfterFeeCharge %v", assets.Value, tokenAmountAfterFeeChargeOnDst)
					}
					sum := (&big.Int{}).Add(assets.Value, assets.Fee)
					if sum.Cmp(tokenAmountBeforeFeeChargeOnDst[index]) != 0 {
						return fmt.Errorf("Expected same value for coinName; Got startEvt.Value+Fee: %v AmountBeforeFeeCharge %v", sum, tokenAmountBeforeFeeChargeOnDst[index])
					}
				}
				return nil
			},
			chain.TransferReceived: func(ev *evt) error {
				if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
					return errors.New("Got nil value for event ")
				}
				receivedEvt, ok := ev.msg.EventLog.(*chain.TransferReceivedEvent)
				if !ok {
					return fmt.Errorf("Expected *chain.TransferReceivedEvent. Got %T", ev.msg.EventLog)
				}
				receivedEvt.To = src.GetBTPAddress(receivedEvt.To)
				if receivedEvt.To != srcAddr {
					return fmt.Errorf("Expected Same Value for DstAddr; Got receivedEvt.To: %v dstAddr: %v", receivedEvt.To, srcAddr)
				}
				if len(receivedEvt.Assets) != len(coinNames) {
					return fmt.Errorf("For single token transfer; Expected single asset; Got %v", len(receivedEvt.Assets))
				}
				for _, assets := range receivedEvt.Assets {
					index := -1
					for i, coinName := range coinNames {
						if coinName == assets.Name {
							index = i
							break
						}
					}
					if index == -1 {
						return fmt.Errorf("Asset name %v not on coinNames list", assets.Name)
					}
					if assets.Value.Cmp(tokenAmountAfterFeeChargeOnDst) != 0 {
						return fmt.Errorf("Expected same value for coinName; Got receivedEvt.Value: %v AmountAfterFeeCharge %v", receivedEvt.Assets[0].Value, tokenAmountAfterFeeChargeOnDst)
					}
				}
				return nil
			},
			chain.TransferEnd: func(ev *evt) error {
				if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
					return errors.New("Got nil value for event ")
				}
				endEvt, ok := ev.msg.EventLog.(*chain.TransferEndEvent)
				if !ok {
					return fmt.Errorf("Expected *chain.TransferEndEvent. Got %T", ev.msg.EventLog)
				}

				if endEvt.Code.String() == "0" {
					ts.logger.Debug("Got Transfer End")
					return nil
				}
				return fmt.Errorf("Unexpected code %v and response %v", endEvt.Code, endEvt.Response)
			},
		})
		if err != nil {
			errs = errors.Wrapf(err, "WaitForTransferEvents %v", err)
			ts.logger.Error(errs)
			return
		}

		ts.logger.Debug("final tally")

		finalSrcBalance := make([]*chain.CoinBalance, len(coinNames))
		finalDstBalance := make([]*chain.CoinBalance, len(coinNames))
		for i, coinName := range coinNames {
			finalSrcBalance[i], err = src.GetCoinBalance(coinName, srcAddr)
			if err != nil {
				errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
				ts.logger.Error(errs)
				return
			}
			finalDstBalance[i], err = dst.GetCoinBalance(coinName, dstAddr)
			if err != nil {
				errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
				ts.logger.Error(errs)
				return
			}
		}
		finalDstNativeCoinBalance, err := dst.GetCoinBalance(dst.NativeCoin(), dstAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		gasSpentOnTxn, err = dst.ChargedGasFee(transferHashOnDst)
		if err != nil {
			errs = errors.Wrapf(err, "GetGasUsed Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		for _, coinName := range coinNames {
			if coinName != dst.NativeCoin() {
				gasSpentOnApprove, err := dst.ChargedGasFee(approveHash[coinName])
				if err != nil {
					errs = errors.Wrapf(err, "GetGasUsed for Approve Err: %v", err)
					ts.logger.Error(errs)
					return
				}
				gasSpentOnTxn.Add(gasSpentOnTxn, gasSpentOnApprove)
			}
		}
		for i, coinName := range coinNames {
			if coinName != dst.NativeCoin() {
				tmpDiff := (&big.Int{}).Sub(intermediateDstBalance[i].UserBalance, finalDstBalance[i].UserBalance)
				if tokenAmountBeforeFeeChargeOnDst[i].Cmp(tmpDiff) != 0 {
					errs = fmt.Errorf("Expected same value for dst balance After transfer, Got TransferAmt %v DstDiffAmt %v", tokenAmountBeforeFeeChargeOnDst[i], tmpDiff)
					ts.logger.Error(errs)
					return
				}
				tmpNativeCoinUsed := (&big.Int{}).Add(nativeCoinAmountBeforeFeeChargeOnDst, gasSpentOnTxn)
				tmpDiff = (&big.Int{}).Sub(initDstNativeCoinBalance.UserBalance, finalDstNativeCoinBalance.UserBalance)
				if tmpNativeCoinUsed.Cmp(tmpDiff) != 0 {
					errs = fmt.Errorf("Expected same value for dst nativeCoin balance after txn; Got GasSpentOnTxn %v srcDiffAmt %v", gasSpentOnTxn, tmpDiff)
					ts.logger.Error(errs)
					return
				}
			} else {
				tmpNativeCoinUsed := (&big.Int{}).Add(tokenAmountBeforeFeeChargeOnDst[i], gasSpentOnTxn)
				tmpDiff := (&big.Int{}).Sub(intermediateDstBalance[i].UserBalance, finalDstBalance[i].UserBalance)
				if tmpNativeCoinUsed.Cmp(tmpDiff) != 0 {
					errs = fmt.Errorf("Expected same value for dst balance After transfer, Got TransferAmt %v DstDiffAmt %v", tmpNativeCoinUsed, tmpDiff)
					ts.logger.Error(errs)
					return
				}
			}
			tmpDiff := (&big.Int{}).Sub(finalSrcBalance[i].UserBalance, intermediateSrcBalance[i].UserBalance)
			if tokenAmountAfterFeeChargeOnDst.Cmp(tmpDiff) != 0 {
				errs = fmt.Errorf("Expected same value for src balance After transfer, Got TransferAmt %v SrcDiffAmt %v", tokenAmountAfterFeeChargeOnDst, tmpDiff)
				ts.logger.Error(errs)
				return
			}
		}
		ts.logger.Debug("Pass")
		return
	},
}
var TransferBiDirection Script = Script{
	Name:        "TransferBiDirectionWithApprove",
	Type:        "Transfer",
	Description: "Transfer Fixed Amount of coin with approve and monitor eventlogs TransferReceived and TransferEnd",
	Callback: func(ctx context.Context, tp *transferPoint, ts *testSuite) (txnRec *txnRecord, errs error) {
		srcChain := tp.SrcChain
		dstChain := tp.DstChain
		coinNames := tp.CoinNames

		txnRec = &txnRecord{
			feeRecords: []*feeRecord{},
			addresses:  make(map[chain.ChainType][]keypair),
		}

		if len(coinNames) != 1 {
			errs = UnsupportedCoinArgs
			ts.logger.Debug(errs)
			return
		}
		coinName := coinNames[0]
		src, tmpOk := ts.clsPerChain[srcChain]
		if !tmpOk {
			errs = fmt.Errorf("Chain %v not found", srcChain)
			ts.logger.Error(errs)
			return
		}
		dst, tmpOk := ts.clsPerChain[dstChain]
		if !tmpOk {
			errs = fmt.Errorf("Chain %v not found", srcChain)
			ts.logger.Error(errs)
			return
		}
		srcKey, srcAddr, err := ts.GetKeyPairs(srcChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetKeyPairs %v", err)
			ts.logger.Error(errs)
			return
		}
		txnRec.addresses[srcChain] = []keypair{{PrivKey: srcKey, PubKey: srcAddr}}

		dstKey, dstAddr, err := ts.GetKeyPairs(dstChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetKeyPairs %v", err)
			ts.logger.Error(errs)
			return
		}
		txnRec.addresses[dstChain] = []keypair{{PrivKey: dstKey, PubKey: dstAddr}}
		ts.logger.Debug("Accounts ", srcAddr, dstAddr)

		tokenAmountAfterFeeChargeOnDst := big.NewInt(1)
		tokenAmountBeforeFeeChargeOnDst, err := ts.getAmountBeforeFeeCharge(dstChain, coinName, tokenAmountAfterFeeChargeOnDst)
		if err != nil {
			errs = errors.Wrapf(err, "getAmountBeforeFeeCharge %v", err)
			ts.logger.Error(errs)
			return
		}
		tokenAmountBeforeFeeChargeOnSrc, err := ts.getAmountBeforeFeeCharge(srcChain, coinName, tokenAmountBeforeFeeChargeOnDst)
		if err != nil {
			errs = errors.Wrapf(err, "getAmountBeforeFeeCharge %v", err)
			ts.logger.Error(errs)
			return
		}
		ts.logger.Debug("Tokens ", tokenAmountAfterFeeChargeOnDst, tokenAmountBeforeFeeChargeOnDst, tokenAmountBeforeFeeChargeOnSrc)

		gasLimitOnSrc := big.NewInt(int64(ts.cfgPerChain[srcChain].GasLimit[chain.TransferCoinInterChainGasLimit]))
		if coinName != src.NativeCoin() {
			gasLimitOnSrc.Add(gasLimitOnSrc, big.NewInt(int64(ts.cfgPerChain[srcChain].GasLimit[chain.ApproveTokenInterChainGasLimit])))
		}
		gp := src.SuggestGasPrice()
		ts.logger.Debug("GP ", gp, gasLimitOnSrc)
		gasFeeOnSrc := (&big.Int{}).Mul(src.SuggestGasPrice(), gasLimitOnSrc)
		gasLimitOnDst := big.NewInt(int64(ts.cfgPerChain[dstChain].GasLimit[chain.TransferCoinInterChainGasLimit]))
		if coinName != dst.NativeCoin() {
			gasLimitOnDst.Add(gasLimitOnDst, big.NewInt(int64(ts.cfgPerChain[dstChain].GasLimit[chain.ApproveTokenInterChainGasLimit])))
		}
		gp = dst.SuggestGasPrice()
		gasFeeOnDst := (&big.Int{}).Mul(gp, gasLimitOnDst)

		ts.logger.Debug("Gas fee on dst and src ", gasFeeOnDst, gasFeeOnSrc)

		if errs = ts.Fund(srcChain, srcAddr, tokenAmountBeforeFeeChargeOnSrc, coinName); errs != nil {

			ts.logger.Debug(errors.Wrapf(err, "srcChain %v, srcAddr %v, amount %v, coinName %v err %v", srcChain, srcAddr, tokenAmountBeforeFeeChargeOnSrc, coinName, errs))
			return
		}
		if errs = ts.Fund(srcChain, srcAddr, gasFeeOnSrc, src.NativeCoin()); errs != nil {

			ts.logger.Debug(errors.Wrapf(err, "srcChain %v, srcAddr %v, amount %v, coinName %v err %v", srcChain, srcAddr, gasFeeOnSrc, src.NativeCoin(), errs))
			return
		}
		if errs = ts.Fund(dstChain, dstAddr, gasFeeOnDst, dst.NativeCoin()); errs != nil {

			ts.logger.Debug(errors.Wrapf(err, "srcChain %v, srcAddr %v, amount %v, coinName %v err %v", srcChain, srcAddr, gasFeeOnDst, dst.NativeCoin(), errs))
			return
		}

		initSrcBalance, err := src.GetCoinBalance(coinName, srcAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		initDstBalance, err := dst.GetCoinBalance(coinName, dstAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		initSrcNativeCoinBalance, err := src.GetCoinBalance(src.NativeCoin(), srcAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		initDstNativeCoinBalance, err := dst.GetCoinBalance(dst.NativeCoin(), dstAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		ts.logger.Debug("InitSrc ", initSrcBalance, tokenAmountBeforeFeeChargeOnSrc)
		ts.logger.Debug("InitSrcNative ", initSrcNativeCoinBalance, tokenAmountBeforeFeeChargeOnSrc)
		ts.logger.Debug("InitDst ", initDstBalance)
		ts.logger.Debug("InitDstNative ", initDstNativeCoinBalance)
		// ApproveToken On Source
		var approveHash string
		if coinName != src.NativeCoin() {
			if approveHash, err = src.Approve(coinName, srcKey, tokenAmountBeforeFeeChargeOnSrc); err != nil {
				errs = errors.Wrapf(err, "Approve Err: %v Hash %v", err, approveHash)
				ts.logger.Error(errs)
				return
			} else {
				if _, err := ts.ValidateTransactionResult(ctx, srcChain, approveHash); err != nil {
					errs = errors.Wrapf(err, "Approve ValidateTransactionResult Err: %v Hash %v", err, approveHash)
					ts.logger.Error(errs)
					return
				}
			}
		}

		transferHashOnSrc, err := src.Transfer(coinName, srcKey, dstAddr, tokenAmountBeforeFeeChargeOnSrc)
		if err != nil {
			errs = errors.Wrapf(err, "Transfer Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		ts.logger.Debug("transferHashOnSrc ", transferHashOnSrc)
		if _, err := ts.ValidateTransactionResult(ctx, srcChain, transferHashOnSrc); err != nil {
			errs = errors.Wrapf(err, "ValidateTransactionResult %v", err)
			ts.logger.Error(errs)
			return
		}

		err = ts.WaitForTransferEvents(ctx, srcChain, dstChain, transferHashOnSrc, map[chain.EventLogType]func(*evt) error{
			chain.TransferStart: func(ev *evt) error {
				if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
					return errors.New("Got nil value for event ")
				}
				startEvt, ok := ev.msg.EventLog.(*chain.TransferStartEvent)
				if !ok {
					return fmt.Errorf("Expected *chain.TransferStartEvent. Got %T", ev.msg.EventLog)
				}
				txnRec.feeRecords = append(txnRec.feeRecords, &feeRecord{
					ChainName: srcChain,
					Sn:        startEvt.Sn,
					Fee:       map[string]*big.Int{startEvt.Assets[0].Name: startEvt.Assets[0].Fee},
				})
				startEvt.From = src.GetBTPAddress(startEvt.From)
				if startEvt.From != srcAddr {
					return fmt.Errorf("Expected Same Value for SrcAddr; Got startEvt.From: %v srcAddr: %v", startEvt.From, srcAddr)
				}
				if startEvt.To != dstAddr {
					return fmt.Errorf("Expected Same Value for DstAddr; Got startEvt.To: %v dstAddr: %v", startEvt.To, dstAddr)
				}
				if len(startEvt.Assets) != 1 {
					return fmt.Errorf("For single token transfer; Expected single asset; Got %v", len(startEvt.Assets))
				}
				if startEvt.Assets[0].Name != coinName {
					return fmt.Errorf("Expected same value for coinName; Got startEvt.Assets.Name: %v coinName %v", startEvt.Assets[0].Name, coinName)
				}
				if startEvt.Assets[0].Value.Cmp(tokenAmountBeforeFeeChargeOnDst) != 0 {
					return fmt.Errorf("Expected same value for coinName; Got startEvt.Value: %v AmountAfterFeeCharge %v", startEvt.Assets[0].Value, tokenAmountBeforeFeeChargeOnDst)
				}
				sum := (&big.Int{}).Add(startEvt.Assets[0].Value, startEvt.Assets[0].Fee)
				if sum.Cmp(tokenAmountBeforeFeeChargeOnSrc) != 0 {
					return fmt.Errorf("Expected same value for coinName; Got startEvt.Value+Fee: %v AmountBeforeFeeCharge %v", sum, tokenAmountBeforeFeeChargeOnSrc)
				}
				return nil
			},
			chain.TransferReceived: func(ev *evt) error {
				if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
					return errors.New("Got nil value for event ")
				}
				receivedEvt, ok := ev.msg.EventLog.(*chain.TransferReceivedEvent)
				if !ok {
					return fmt.Errorf("Expected *chain.TransferReceivedEvent. Got %T", ev.msg.EventLog)
				}
				receivedEvt.To = dst.GetBTPAddress(receivedEvt.To)
				if receivedEvt.To != dstAddr {
					return fmt.Errorf("Expected Same Value for DstAddr; Got receivedEvt.To: %v dstAddr: %v", receivedEvt.To, dstAddr)
				}
				if len(receivedEvt.Assets) != 1 {
					return fmt.Errorf("For single token transfer; Expected single asset; Got %v", len(receivedEvt.Assets))
				}
				if receivedEvt.Assets[0].Name != coinName {
					return fmt.Errorf("Expected same value for coinName; Got receivedEvt.Assets.Name: %v coinName %v", receivedEvt.Assets[0].Name, coinName)
				}
				if receivedEvt.Assets[0].Value.Cmp(tokenAmountBeforeFeeChargeOnDst) != 0 {
					return fmt.Errorf("Expected same value for coinName; Got receivedEvt.Value: %v AmountAfterFeeCharge %v", receivedEvt.Assets[0].Value, tokenAmountBeforeFeeChargeOnDst)
				}
				return nil
			},
			chain.TransferEnd: func(ev *evt) error {
				if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
					return errors.New("Got nil value for event ")
				}
				endEvt, ok := ev.msg.EventLog.(*chain.TransferEndEvent)
				if !ok {
					return fmt.Errorf("Expected *chain.TransferEndEvent. Got %T", ev.msg.EventLog)
				}
				if endEvt.Code.String() == "0" {
					ts.logger.Debug("Got Transfer End")
					return nil
				}
				return fmt.Errorf("Unexpected code %v and response %v", endEvt.Code, endEvt.Response)
			},
		})
		if err != nil {
			errs = errors.Wrapf(err, "WaitForTransferEvents %v", err)
			ts.logger.Error(errs)
			return
		}

		intermediateSrcBalance, err := src.GetCoinBalance(coinName, srcAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		intermediateSrcNativeCoinBalance, err := src.GetCoinBalance(src.NativeCoin(), srcAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		intermediateDstBalance, err := dst.GetCoinBalance(coinName, dstAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		gasSpentOnTxn, err := src.ChargedGasFee(transferHashOnSrc)
		if err != nil {
			errs = errors.Wrapf(err, "ChargedGasFee For Transfer Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		if coinName != src.NativeCoin() {
			gasSpentOnApprove, err := src.ChargedGasFee(approveHash)
			if err != nil {
				errs = errors.Wrapf(err, "ChargedGasFee for Approve Err: %v", err)
				ts.logger.Error(errs)
				return
			}
			gasSpentOnTxn.Add(gasSpentOnTxn, gasSpentOnApprove)
			tmpDiff := (&big.Int{}).Sub(initSrcBalance.UserBalance, intermediateSrcBalance.UserBalance)
			if tokenAmountBeforeFeeChargeOnSrc.Cmp(tmpDiff) != 0 {
				errs = fmt.Errorf("Expected same value for src balance After transfer, Got TransferAmt %v SrcDiffAmt %v", tokenAmountBeforeFeeChargeOnSrc, tmpDiff)
				ts.logger.Error(errs)
				return
			}
			tmpDiff = (&big.Int{}).Sub(initSrcNativeCoinBalance.UserBalance, intermediateSrcNativeCoinBalance.UserBalance)
			if gasSpentOnTxn.Cmp(tmpDiff) != 0 {
				errs = fmt.Errorf("Expected same value for src nativeCoin balance after txn; Got GasSpentOnTxn %v srcDiffAmt %v", gasSpentOnTxn, tmpDiff)
				ts.logger.Error(errs)
				return
			}
		} else {
			tmpNativeCoinUsed := (&big.Int{}).Add(tokenAmountBeforeFeeChargeOnSrc, gasSpentOnTxn)
			tmpDiff := (&big.Int{}).Sub(initSrcBalance.UserBalance, intermediateSrcBalance.UserBalance)
			if tmpNativeCoinUsed.Cmp(tmpDiff) != 0 {
				errs = fmt.Errorf("Expected same value for src balance After transfer, Got TransferAmt %v SrcDiffAmt %v", tmpNativeCoinUsed, tmpDiff)
				ts.logger.Error(errs)
				return
			}
		}
		tmpDiff := (&big.Int{}).Sub(intermediateDstBalance.UserBalance, initDstBalance.UserBalance)
		if tokenAmountBeforeFeeChargeOnDst.Cmp(tmpDiff) != 0 {
			errs = fmt.Errorf("Expected same value for dst balance After transfer, Got TransferAmt %v DstDiffAmt %v", tokenAmountBeforeFeeChargeOnDst, tmpDiff)
			ts.logger.Error(errs)
			return
		}

		ts.logger.Debug("IntermediateSrc ", intermediateSrcBalance)
		ts.logger.Debug("IntermediateDst ", intermediateDstBalance, tokenAmountBeforeFeeChargeOnDst)

		if coinName != dst.NativeCoin() {
			if approveHash, err = dst.Approve(coinName, dstKey, tokenAmountBeforeFeeChargeOnDst); err != nil {
				errs = errors.Wrapf(err, "Approve Err: %v Hash %v", err, approveHash)
				ts.logger.Error(errs)
				return
			} else {
				if _, err := ts.ValidateTransactionResult(ctx, dstChain, approveHash); err != nil {
					errs = errors.Wrapf(err, "Approve ValidateTransactionResult Err: %v Hash %v", err, approveHash)
					ts.logger.Error(errs)
					return
				}
			}
		}

		transferHashOnDst, err := dst.Transfer(coinName, dstKey, srcAddr, tokenAmountBeforeFeeChargeOnDst)
		if err != nil {
			errs = errors.Wrapf(err, "Transfer Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		ts.logger.Debug("transferHashOnDst ", transferHashOnDst)
		if _, err := ts.ValidateTransactionResult(ctx, dstChain, transferHashOnDst); err != nil {
			errs = errors.Wrapf(err, "ValidateTransactionResult %v", err)
			ts.logger.Error(errs)
			return
		}

		err = ts.WaitForTransferEvents(ctx, dstChain, srcChain, transferHashOnDst, map[chain.EventLogType]func(*evt) error{
			chain.TransferStart: func(ev *evt) error {
				if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
					return errors.New("Got nil value for event ")
				}
				startEvt, ok := ev.msg.EventLog.(*chain.TransferStartEvent)
				if !ok {
					return fmt.Errorf("Expected *chain.TransferStartEvent. Got %T", ev.msg.EventLog)
				}
				txnRec.feeRecords = append(txnRec.feeRecords, &feeRecord{
					ChainName: dstChain,
					Sn:        startEvt.Sn,
					Fee:       map[string]*big.Int{startEvt.Assets[0].Name: startEvt.Assets[0].Fee},
				})
				startEvt.From = dst.GetBTPAddress(startEvt.From)
				if startEvt.From != dstAddr {
					return fmt.Errorf("Expected Same Value for SrcAddr; Got startEvt.From: %v srcAddr: %v", startEvt.From, dstAddr)
				}
				if startEvt.To != srcAddr {
					return fmt.Errorf("Expected Same Value for DstAddr; Got startEvt.To: %v dstAddr: %v", startEvt.To, srcAddr)
				}
				if len(startEvt.Assets) != 1 {
					return fmt.Errorf("For single token transfer; Expected single asset; Got %v", len(startEvt.Assets))
				}
				if startEvt.Assets[0].Name != coinName {
					return fmt.Errorf("Expected same value for coinName; Got startEvt.Assets.Name: %v coinName %v", startEvt.Assets[0].Name, coinName)
				}
				if startEvt.Assets[0].Value.Cmp(tokenAmountAfterFeeChargeOnDst) != 0 {
					return fmt.Errorf("Expected same value for coinName; Got startEvt.Value: %v AmountAfterFeeCharge %v", startEvt.Assets[0].Value, tokenAmountAfterFeeChargeOnDst)
				}
				sum := (&big.Int{}).Add(startEvt.Assets[0].Value, startEvt.Assets[0].Fee)
				if sum.Cmp(tokenAmountBeforeFeeChargeOnDst) != 0 {
					return fmt.Errorf("Expected same value for coinName; Got startEvt.Value+Fee: %v AmountBeforeFeeCharge %v", sum, tokenAmountBeforeFeeChargeOnDst)
				}
				return nil
			},
			chain.TransferReceived: func(ev *evt) error {
				if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
					return errors.New("Got nil value for event ")
				}
				receivedEvt, ok := ev.msg.EventLog.(*chain.TransferReceivedEvent)
				if !ok {
					return fmt.Errorf("Expected *chain.TransferReceivedEvent. Got %T", ev.msg.EventLog)
				}
				receivedEvt.To = src.GetBTPAddress(receivedEvt.To)
				if receivedEvt.To != srcAddr {
					return fmt.Errorf("Expected Same Value for DstAddr; Got receivedEvt.To: %v dstAddr: %v", receivedEvt.To, srcAddr)
				}
				if len(receivedEvt.Assets) != 1 {
					return fmt.Errorf("For single token transfer; Expected single asset; Got %v", len(receivedEvt.Assets))
				}
				if receivedEvt.Assets[0].Name != coinName {
					return fmt.Errorf("Expected same value for coinName; Got receivedEvt.Assets.Name: %v coinName %v", receivedEvt.Assets[0].Name, coinName)
				}
				if receivedEvt.Assets[0].Value.Cmp(tokenAmountAfterFeeChargeOnDst) != 0 {
					return fmt.Errorf("Expected same value for coinName; Got receivedEvt.Value: %v AmountAfterFeeCharge %v", receivedEvt.Assets[0].Value, tokenAmountAfterFeeChargeOnDst)
				}
				return nil
			},
			chain.TransferEnd: func(ev *evt) error {
				if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
					return errors.New("Got nil value for event ")
				}
				endEvt, ok := ev.msg.EventLog.(*chain.TransferEndEvent)
				if !ok {
					return fmt.Errorf("Expected *chain.TransferEndEvent. Got %T", ev.msg.EventLog)
				}
				if endEvt.Code.String() == "0" {
					ts.logger.Debug("Got Transfer End")
					return nil
				}
				return fmt.Errorf("Unexpected code %v and response %v", endEvt.Code, endEvt.Response)
			},
		})
		if err != nil {
			errs = errors.Wrapf(err, "WaitForTransferEvents %v", err)
			ts.logger.Error(errs)
			return
		}

		finalSrcBalance, err := src.GetCoinBalance(coinName, srcAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		finalDstBalance, err := dst.GetCoinBalance(coinName, dstAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		finalDstNativeCoinBalance, err := dst.GetCoinBalance(dst.NativeCoin(), dstAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		gasSpentOnTxn, err = dst.ChargedGasFee(transferHashOnDst)
		if err != nil {
			errs = errors.Wrapf(err, "GetGasUsed Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		if coinName != dst.NativeCoin() {
			gasSpentOnApprove, err := dst.ChargedGasFee(approveHash)
			if err != nil {
				errs = errors.Wrapf(err, "GetGasUsed for Approve Err: %v", err)
				ts.logger.Error(errs)
				return
			}
			gasSpentOnTxn.Add(gasSpentOnTxn, gasSpentOnApprove)
			tmpDiff = (&big.Int{}).Sub(intermediateDstBalance.UserBalance, finalDstBalance.UserBalance)
			if tokenAmountBeforeFeeChargeOnDst.Cmp(tmpDiff) != 0 {
				errs = fmt.Errorf("Expected same value for dst balance After transfer, Got TransferAmt %v DstDiffAmt %v", tokenAmountBeforeFeeChargeOnDst, tmpDiff)
				ts.logger.Error(errs)
				return
			}
			tmpDiff = (&big.Int{}).Sub(initDstNativeCoinBalance.UserBalance, finalDstNativeCoinBalance.UserBalance)
			if gasSpentOnTxn.Cmp(tmpDiff) != 0 {
				errs = fmt.Errorf("Expected same value for dst nativeCoin balance after txn; Got GasSpentOnTxn %v srcDiffAmt %v", gasSpentOnTxn, tmpDiff)
				ts.logger.Error(errs)
				return
			}
		} else {
			tmpNativeCoinUsed := (&big.Int{}).Add(tokenAmountBeforeFeeChargeOnDst, gasSpentOnTxn)
			tmpDiff = (&big.Int{}).Sub(intermediateDstBalance.UserBalance, finalDstBalance.UserBalance)
			if tmpNativeCoinUsed.Cmp(tmpDiff) != 0 {
				errs = fmt.Errorf("Expected same value for dst balance After transfer, Got TransferAmt %v DstDiffAmt %v", tmpNativeCoinUsed, tmpDiff)
				ts.logger.Error(errs)
				return
			}
		}
		tmpDiff = (&big.Int{}).Sub(finalSrcBalance.UserBalance, intermediateSrcBalance.UserBalance)
		if tokenAmountAfterFeeChargeOnDst.Cmp(tmpDiff) != 0 {
			errs = fmt.Errorf("Expected same value for src balance After transfer, Got TransferAmt %v SrcDiffAmt %v", tokenAmountAfterFeeChargeOnDst, tmpDiff)
			ts.logger.Error(errs)
			return
		}
		return
	},
}
var TransferEqualToFee Script = Script{
	Name:        "TransferEqualToFee",
	Description: "Transfer equal to fee",
	Type:        "Transfer",
	Callback: func(ctx context.Context, tp *transferPoint, ts *testSuite) (txnRec *txnRecord, errs error) {
		srcChain := tp.SrcChain
		dstChain := tp.DstChain
		coinNames := tp.CoinNames

		txnRec = &txnRecord{
			feeRecords: []*feeRecord{},
			addresses:  make(map[chain.ChainType][]keypair),
		}

		if len(coinNames) != 1 {
			errs = UnsupportedCoinArgs
			ts.logger.Debug(errs)
			return
		}

		coinName := coinNames[0]
		src, dst, err := ts.GetChainPair(srcChain, dstChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetChainPair %v", err)
			ts.logger.Error(errs)
			return
		}

		srcKey, srcAddr, err := ts.GetKeyPairs(srcChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetKeyPairs %v", err)
			ts.logger.Error(errs)
			return
		}
		txnRec.addresses[srcChain] = []keypair{{PrivKey: srcKey, PubKey: srcAddr}}
		dstKey, dstAddr, err := ts.GetKeyPairs(dstChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetKeyPairs %v", err)
			ts.logger.Error(errs)
			return
		}
		txnRec.addresses[dstChain] = []keypair{{PrivKey: dstKey, PubKey: dstAddr}}

		netTransferrableAmount := big.NewInt(0)
		userSuppliedAmount, err := ts.getAmountBeforeFeeCharge(srcChain, coinName, netTransferrableAmount)
		gasLimitOnSrc := big.NewInt(int64(ts.cfgPerChain[srcChain].GasLimit[chain.TransferCoinInterChainGasLimit]))
		if coinName != src.NativeCoin() {
			gasLimitOnSrc.Add(gasLimitOnSrc, big.NewInt(int64(ts.cfgPerChain[srcChain].GasLimit[chain.ApproveTokenInterChainGasLimit])))
		}
		srcGasPrice := src.SuggestGasPrice()
		gasFeeOnSrc := (&big.Int{}).Mul(srcGasPrice, gasLimitOnSrc)

		if errs = ts.Fund(srcChain, srcAddr, userSuppliedAmount, coinName); errs != nil {

			ts.logger.Debug(errors.Wrapf(errs, "srcChain %v, srcAddr %v, coinName %v err %v", srcChain, srcAddr, coinName, errs))
			return
		}
		if errs = ts.Fund(srcChain, srcAddr, gasFeeOnSrc, src.NativeCoin()); errs != nil {

			ts.logger.Debug(errors.Wrapf(errs, "srcChain %v, srcAddr %v, coinName %v err %v", srcChain, srcAddr, coinName, errs))
			return
		}

		initSrcBalance, err := src.GetCoinBalance(coinName, srcAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		initDstBalance, err := dst.GetCoinBalance(coinName, dstAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		initSrcNativeCoinBalance, err := src.GetCoinBalance(src.NativeCoin(), srcAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}

		// Approve
		var approveHash string
		if coinName != src.NativeCoin() {
			if approveHash, err = src.Approve(coinName, srcKey, userSuppliedAmount); err != nil {
				errs = errors.Wrapf(err, "Approve Err: %v Hash %v", err, approveHash)
				ts.logger.Error(errs)
				return
			} else {
				if _, err := ts.ValidateTransactionResult(ctx, srcChain, approveHash); err != nil {
					errs = errors.Wrapf(err, "Approve ValidateTransactionResult Err: %v Hash %v", err, approveHash)
					ts.logger.Error(errs)
					return
				}
			}
		}

		transferHashOnSrc, err := src.Transfer(coinName, srcKey, dstAddr, userSuppliedAmount)
		if err != nil {
			errs = errors.Wrapf(err, "Transfer Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		ts.logger.Debug("transferHashOnSrc ", transferHashOnSrc)
		if _, errs = ts.ValidateTransactionResult(ctx, srcChain, transferHashOnSrc); errs != nil {
			if errs.Error() == StatusCodeZero.Error() {
				finalSrcBalance, err := src.GetCoinBalance(coinName, srcAddr)
				if err != nil {
					errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
					ts.logger.Error(errs)
					return
				}
				finalSrcNativeCoinBalance, err := src.GetCoinBalance(src.NativeCoin(), srcAddr)
				if err != nil {
					errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
					ts.logger.Error(errs)
					return
				}
				gasSpentOnTxn, err := src.ChargedGasFee(transferHashOnSrc)
				if err != nil {
					errs = errors.Wrapf(err, "GetGasUsed For Transfer Err: %v", err)
					ts.logger.Error(errs)
					return
				}
				if coinName != src.NativeCoin() {
					if initSrcBalance.TotalBalance.Cmp(finalSrcBalance.TotalBalance) != 0 {
						errs = fmt.Errorf("Expected Same, Got Different. finalSrcBalance %v initialSrcBalance %v", finalSrcBalance.TotalBalance, initSrcBalance.TotalBalance)
						ts.logger.Error(errs)
						return
					}
					gasSpentOnApprove, err := src.ChargedGasFee(approveHash)
					if err != nil {
						errs = errors.Wrapf(err, "GetGasUsed for Approve Err: %v", err)
						ts.logger.Error(errs)
						return
					}
					gasSpentOnTxn.Add(gasSpentOnTxn, gasSpentOnApprove)
					tmpDiff := (&big.Int{}).Sub(initSrcNativeCoinBalance.UserBalance, finalSrcNativeCoinBalance.UserBalance)
					if gasSpentOnTxn.Cmp(tmpDiff) != 0 {
						errs = fmt.Errorf("Expected same value; Got different GasSpent %v NativeCoinBalanceDiff %v", gasSpentOnTxn, tmpDiff)
						ts.logger.Error(errs)
						return
					}
				} else {
					tmpDiff := (&big.Int{}).Sub(initSrcNativeCoinBalance.UserBalance, finalSrcNativeCoinBalance.UserBalance)
					if gasSpentOnTxn.Cmp(tmpDiff) != 0 {
						errs = fmt.Errorf("Expected same value; Got different GasSpent %v NativeCoinBalanceDiff %v", gasSpentOnTxn, tmpDiff)
						ts.logger.Error(errs)
						return
					}
				}
				errs = nil
				return
			}
			errs = errors.Wrapf(err, "ValidateTransactionResult Got Unexpected Error: %v", err)
			ts.logger.Error(errs)
			return
		}

		err = ts.WaitForTransferEvents(ctx, srcChain, dstChain, transferHashOnSrc, map[chain.EventLogType]func(*evt) error{
			chain.TransferStart: func(ev *evt) error {
				if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
					return errors.New("Got nil value for event ")
				}
				startEvt, ok := ev.msg.EventLog.(*chain.TransferStartEvent)
				if !ok {
					return fmt.Errorf("Expected *chain.TransferStartEvent. Got %T", ev.msg.EventLog)
				}
				txnRec.feeRecords = append(txnRec.feeRecords, &feeRecord{
					ChainName: srcChain,
					Sn:        startEvt.Sn,
					Fee:       map[string]*big.Int{startEvt.Assets[0].Name: startEvt.Assets[0].Fee},
				})
				startEvt.From = src.GetBTPAddress(startEvt.From)
				if startEvt.From != srcAddr {
					return fmt.Errorf("Expected Same Value for SrcAddr; Got startEvt.From: %v srcAddr: %v", startEvt.From, srcAddr)
				}
				if startEvt.To != dstAddr {
					return fmt.Errorf("Expected Same Value for DstAddr; Got startEvt.To: %v dstAddr: %v", startEvt.To, dstAddr)
				}
				if len(startEvt.Assets) != 1 {
					return fmt.Errorf("For single token transfer; Expected single asset; Got %v", len(startEvt.Assets))
				}
				if startEvt.Assets[0].Name != coinName {
					return fmt.Errorf("Expected same value for coinName; Got startEvt.Assets.Name: %v coinName %v", startEvt.Assets[0].Name, coinName)
				}
				if startEvt.Assets[0].Value.Cmp(netTransferrableAmount) != 0 {
					return fmt.Errorf("Expected same value for coinName; Got startEvt.Value: %v AmountAfterFeeCharge %v", startEvt.Assets[0].Value, netTransferrableAmount)
				}
				sum := (&big.Int{}).Add(startEvt.Assets[0].Value, startEvt.Assets[0].Fee)
				if sum.Cmp(userSuppliedAmount) != 0 {
					return fmt.Errorf("Expected same value for coinName; Got startEvt.Value+Fee: %v AmountBeforeFeeCharge %v", sum, userSuppliedAmount)
				}
				return nil
			},
			chain.TransferEnd: func(ev *evt) error {
				if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
					return errors.New("Got nil value for event ")
				}
				endEvt, ok := ev.msg.EventLog.(*chain.TransferEndEvent)
				if !ok {
					return fmt.Errorf("Expected *chain.TransferEndEvent Got %T", ev.msg.EventLog)
				}
				if endEvt.Code.String() != "1" {
					return fmt.Errorf("Expected error code (1) Got %v", endEvt.Code.String())
				}
				return nil
			},
		})
		if err != nil {
			errs = errors.Wrapf(err, "WaitForTransferEvents %v", err)
			ts.logger.Error(errs)
			return
		}
		finalSrcBalance, err := src.GetCoinBalance(coinName, srcAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		finalDstBalance, err := dst.GetCoinBalance(coinName, dstAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		finalSrcNativeCoinBalance, err := src.GetCoinBalance(src.NativeCoin(), srcAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		gasSpentOnTxn, err := src.ChargedGasFee(transferHashOnSrc)
		if err != nil {
			errs = errors.Wrapf(err, "GetGasUsed For Transfer Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		if coinName != src.NativeCoin() {
			tmpDiff := (&big.Int{}).Sub(initSrcBalance.UserBalance, finalSrcBalance.UserBalance)
			feeCharged := (&big.Int{}).Sub(userSuppliedAmount, netTransferrableAmount)
			if tmpDiff.Cmp(feeCharged) != 0 {
				errs = fmt.Errorf("Expected same value; Got different feeCharged %v BalanceDiff %v", feeCharged, tmpDiff)
				ts.logger.Error(errs)
				return
			}
			gasSpentOnApprove, err := src.ChargedGasFee(approveHash)
			if err != nil {
				errs = errors.Wrapf(err, "GetGasUsed for Approve Err: %v", err)
				ts.logger.Error(errs)
				return
			}
			gasSpentOnTxn.Add(gasSpentOnTxn, gasSpentOnApprove)
			tmpDiff = (&big.Int{}).Sub(initSrcNativeCoinBalance.UserBalance, finalSrcNativeCoinBalance.UserBalance)
			if gasSpentOnTxn.Cmp(tmpDiff) != 0 {
				errs = fmt.Errorf("Expected same value; Got different GasSpent %v NativeCoinBalanceDiff %v", gasSpentOnTxn, tmpDiff)
				ts.logger.Error(errs)
				return
			}
		} else {
			feeCharged := (&big.Int{}).Sub(userSuppliedAmount, netTransferrableAmount)
			tmpNativeCoinUsed := (&big.Int{}).Add(feeCharged, gasSpentOnTxn)
			tmpDiff := (&big.Int{}).Sub(initSrcBalance.UserBalance, finalSrcBalance.UserBalance)
			if tmpNativeCoinUsed.Cmp(tmpDiff) != 0 {
				errs = fmt.Errorf("Expected same, Got Different. NativeCoinUsed %v SrcDiffAmt %v", tmpNativeCoinUsed, tmpDiff)
				ts.logger.Error(errs)
				return
			}
		}
		if initDstBalance.UserBalance.Cmp(finalDstBalance.UserBalance) != 0 {
			errs = fmt.Errorf("Epected same; Got Different. initDstBalance %v finalDstBalance %v", initDstBalance, finalDstBalance)
			ts.logger.Error(errs)
			return
		}
		errs = err
		ts.logger.Error(errs)
		return
	},
}
var TransferFromBlackListedSrcAddress Script = Script{
	Name:        "TransferFromBlackListedSrcAddress",
	Description: "Transfer from BlackListed Source Address",
	Type:        "Transfer",
	Callback: func(ctx context.Context, tp *transferPoint, ts *testSuite) (txnRec *txnRecord, errs error) {
		srcChain := tp.SrcChain
		dstChain := tp.DstChain
		coinNames := tp.CoinNames

		txnRec = &txnRecord{
			feeRecords: []*feeRecord{},
			addresses:  make(map[chain.ChainType][]keypair),
		}

		if len(coinNames) != 1 {
			errs = UnsupportedCoinArgs
			ts.logger.Debug(errs)
			return
		}
		coinName := coinNames[0]
		src, _, err := ts.GetChainPair(srcChain, dstChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetChainPair %v", err)
			ts.logger.Error(errs)
			return
		}

		srcKey, srcAddr, err := ts.GetKeyPairs(srcChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetKeyPairs %v", err)
			ts.logger.Error(errs)
			return
		}
		txnRec.addresses[srcChain] = []keypair{{PrivKey: srcKey, PubKey: srcAddr}}
		dstKey, dstAddr, err := ts.GetKeyPairs(dstChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetKeyPairs %v", err)
			ts.logger.Error(errs)
			return
		}
		txnRec.addresses[dstChain] = []keypair{{PrivKey: dstKey, PubKey: dstAddr}}

		netTransferrableAmount := big.NewInt(1)
		userSuppliedAmount, err := ts.getAmountBeforeFeeCharge(srcChain, coinName, netTransferrableAmount)
		gasLimitOnSrc := big.NewInt(int64(ts.cfgPerChain[srcChain].GasLimit[chain.TransferCoinInterChainGasLimit]))
		if coinName != src.NativeCoin() {
			gasLimitOnSrc.Add(gasLimitOnSrc, big.NewInt(int64(ts.cfgPerChain[srcChain].GasLimit[chain.ApproveTokenInterChainGasLimit])))
		}
		gasFeeOnSrc := (&big.Int{}).Mul(src.SuggestGasPrice(), gasLimitOnSrc)

		if errs = ts.Fund(srcChain, srcAddr, (&big.Int{}).Mul(userSuppliedAmount, big.NewInt(2)), coinName); errs != nil {

			ts.logger.Debug(errors.Wrapf(errs, "srcChain %v, srcAddr %v, coinName %v err %v", srcChain, srcAddr, coinName, errs))
			return
		}
		if errs = ts.Fund(srcChain, srcAddr, (&big.Int{}).Mul(gasFeeOnSrc, big.NewInt(2)), src.NativeCoin()); errs != nil {

			ts.logger.Debug(errors.Wrapf(errs, "srcChain %v, srcAddr %v, coinName %v err %v", srcChain, srcAddr, coinName, errs))
			return
		}

		// Approve
		var approveHash string
		if coinName != src.NativeCoin() {
			if approveHash, err = src.Approve(coinName, srcKey, userSuppliedAmount); err != nil {
				errs = errors.Wrapf(err, "Approve Err: %v Hash %v", err, approveHash)
				ts.logger.Error(errs)
				return
			} else {
				if _, err := ts.ValidateTransactionResult(ctx, srcChain, approveHash); err != nil {
					errs = errors.Wrapf(err, "Approve ValidateTransactionResult Err: %v Hash %v", err, approveHash)
					ts.logger.Error(errs)
					return
				}
			}
		}

		transferHashOnSrc, err := src.Transfer(coinName, srcKey, dstAddr, userSuppliedAmount)
		if err != nil {
			errs = errors.Wrapf(err, "Transfer Err: %v", err)
			ts.logger.Error(errs)
			return
		}

		ts.logger.Debug("transferHashOnSrc ", transferHashOnSrc)
		if _, err := ts.ValidateTransactionResult(ctx, srcChain, transferHashOnSrc); err != nil {
			errs = errors.Wrapf(err, "ValidateTransactionResult %v", err)
			ts.logger.Error(errs)
			return
		}
		err = ts.WaitForTransferEvents(ctx, srcChain, dstChain, transferHashOnSrc, map[chain.EventLogType]func(*evt) error{
			chain.TransferStart: func(ev *evt) error {
				if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
					return errors.New("Got nil value for event ")
				}
				startEvt, ok := ev.msg.EventLog.(*chain.TransferStartEvent)
				if !ok {
					return fmt.Errorf("Expected *chain.TransferStartEvent. Got %T", ev.msg.EventLog)
				}
				txnRec.feeRecords = append(txnRec.feeRecords, &feeRecord{
					ChainName: srcChain,
					Sn:        startEvt.Sn,
					Fee:       map[string]*big.Int{startEvt.Assets[0].Name: startEvt.Assets[0].Fee},
				})

				return nil
			},
			chain.TransferEnd: func(ev *evt) error {
				if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
					return errors.New("Got nil value for event ")
				}
				endEvt, ok := ev.msg.EventLog.(*chain.TransferEndEvent)
				if !ok {
					return fmt.Errorf("Expected *chain.TransferEndEvent Got %T", ev.msg.EventLog)
				}

				if endEvt.Code.String() != "0" {
					return fmt.Errorf("Expected code 0 Got %v", endEvt.Code.String())
				}
				return nil
			},
		})
		if err != nil {
			errs = errors.Wrapf(err, "WaitForTransferEvents %v", err)
			ts.logger.Error(errs)
			return
		}

		ts.logger.Debug("Start Add to blacklist")

		fCfg, err := ts.GetFullConfigAPI()
		if err != nil {
			errs = errors.Wrapf(err, "GetFullConfigAPI %v", err)
			ts.logger.Error(errs)
			return
		}
		fCfgOwnerKey := ts.FullConfigAPIsOwner()
		stdCfg, err := ts.GetStandardConfigAPI(srcChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetStandardConfigAPI %v", err)
			ts.logger.Error(errs)
			return
		}
		blsSrcNet, blsSrcAddr := ts.NetAddr(srcAddr)
		blackListAddHash, err := fCfg.AddBlackListAddress(fCfgOwnerKey, blsSrcNet, []string{blsSrcAddr})
		if err != nil {
			errs = errors.Wrapf(err, "AddBlackListAddress %v", err)
			ts.logger.Error(errs)
			return
		}
		if _, err := ts.ValidateTransactionResult(ctx, ts.FullConfigAPIChain(), blackListAddHash); err != nil {
			errs = errors.Wrapf(err, "ValidateTransactionResult %v", err)
			ts.logger.Error(errs)
			return
		}
		isBlackListed, err := fCfg.IsUserBlackListed(blsSrcNet, blsSrcAddr)
		if err != nil {
			errs = errors.Wrapf(err, "isBlackListed %v", err)
			ts.logger.Error(errs)
			return
		} else if err == nil && !isBlackListed {
			errs = fmt.Errorf("Expected addr ( %v , %v ) to be blacklisted, but was not", blsSrcNet, blsSrcAddr)
			ts.logger.Error(errs)
			return
		}
		responseChain := srcChain
		if responseChain == ts.FullConfigAPIChain() {
			responseChain = dstChain
		}

		if srcChain != ts.FullConfigAPIChain() {
			err = ts.WaitForConfigResponse(ctx, chain.AddToBlacklistRequest, chain.BlacklistResponse, responseChain, blackListAddHash,
				map[chain.EventLogType]func(event *evt) error{
					chain.AddToBlacklistRequest: func(ev *evt) error {
						if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
							return errors.New("Got nil value for event ")
						}
						reqEvt, ok := ev.msg.EventLog.(*chain.AddToBlacklistRequestEvent)
						if !ok {
							return fmt.Errorf("Expected *chain.AddToBlacklistRequestEvent. Got %T", ev.msg.EventLog)
						}
						txnRec.feeRecords = append(txnRec.feeRecords, &feeRecord{
							ChainName: ts.FullConfigAPIChain(),
							Sn:        reqEvt.Sn,
							Fee:       map[string]*big.Int{},
						})
						if reqEvt.Net != blsSrcNet {
							return fmt.Errorf("Expected same; Got different reqEvt.Net %v DstNet %v", reqEvt.Net, blsSrcNet)
						}
						if len(reqEvt.Addrs) != 1 {
							return fmt.Errorf("Expected reqEvt.AddrsLen 1; Got %v", len(reqEvt.Addrs))
						}
						if strings.ToLower(reqEvt.Addrs[0]) != strings.ToLower(blsSrcAddr) {
							return fmt.Errorf("Expected same; Got Different reqEvt.Addrs %v DstAddr %v", reqEvt.Addrs[0], blsSrcAddr)
						}
						return nil
					},
					chain.BlacklistResponse: func(ev *evt) error {
						if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
							return errors.New("Got nil value for event ")
						}
						resEvt, ok := ev.msg.EventLog.(*chain.BlacklistResponseEvent)
						if !ok {
							return fmt.Errorf("Expected *chain.BlacklistResponseEvent. Got %T", ev.msg.EventLog)
						}

						if resEvt.Code != 0 {
							return fmt.Errorf("Expected Code 0; Got Sn %v Code %v Msg %v", resEvt.Sn, resEvt.Code, resEvt.Msg)
						}
						return nil
					},
				},
			)
			if err != nil {
				errs = errors.Wrapf(err, "WaitForConfigResponse %v", err)
				ts.logger.Error(errs)
				return
			}
			isBlackListed, err = stdCfg.IsUserBlackListed(blsSrcNet, blsSrcAddr)
			if err != nil {
				errs = errors.Wrapf(err, "isBlackListed %v", err)
				ts.logger.Error(errs)
				return
			} else if err == nil && !isBlackListed {
				errs = fmt.Errorf("Expected addr ( %v , %v ) to be blacklisted, but was not", blsSrcNet, blsSrcAddr)
				ts.logger.Error(errs)
				return
			}
		}

		ts.logger.Debug("Send to blacklist")

		if coinName != src.NativeCoin() {
			if approveHash, err = src.Approve(coinName, srcKey, userSuppliedAmount); err != nil {
				errs = errors.Wrapf(err, "Approve Err: %v Hash %v", err, approveHash)
				ts.logger.Error(errs)
				return
			} else {
				if _, err := ts.ValidateTransactionResult(ctx, srcChain, approveHash); err != nil && err.Error() != StatusCodeZero.Error() {
					errs = errors.Wrapf(err, "Approve ValidateTransactionResult Err: %v Hash %v", err, approveHash)
					ts.logger.Error(errs)
					return
				}
			}
		}

		transferHashOnSrc, err = src.Transfer(coinName, srcKey, dstAddr, userSuppliedAmount)
		if err != nil {
			errs = errors.Wrapf(err, "Transfer Err: %v", err)
			ts.logger.Error(errs)
			return
		}

		ts.logger.Debug("transferHashOnSrc ", transferHashOnSrc)
		if _, err := ts.ValidateTransactionResult(ctx, srcChain, transferHashOnSrc); err == nil {

			err = ts.WaitForTransferEvents(ctx, srcChain, dstChain, transferHashOnSrc, map[chain.EventLogType]func(*evt) error{
				chain.TransferStart: func(ev *evt) error {
					if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
						return errors.New("Got nil value for event ")
					}
					startEvt, ok := ev.msg.EventLog.(*chain.TransferStartEvent)
					if !ok {
						return fmt.Errorf("Expected *chain.TransferStartEvent. Got %T", ev.msg.EventLog)
					}
					txnRec.feeRecords = append(txnRec.feeRecords, &feeRecord{
						ChainName: srcChain,
						Sn:        startEvt.Sn,
						Fee:       map[string]*big.Int{startEvt.Assets[0].Name: startEvt.Assets[0].Fee},
					})

					return nil
				},
				chain.TransferEnd: func(ev *evt) error {
					if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
						return errors.New("Got nil value for event ")
					}
					endEvt, ok := ev.msg.EventLog.(*chain.TransferEndEvent)
					if !ok {
						return fmt.Errorf("Expected *chain.TransferEndEvent Got %T", ev.msg.EventLog)
					}

					if endEvt.Code.String() != "1" {
						return fmt.Errorf("Expected error code (1) Got %v", endEvt.Code.String())
					}
					return nil
				},
			})
			if err != nil {
				errs = errors.Wrapf(err, "WaitForTransferEvents %v", err)
				ts.logger.Error(errs)
				return
			}
		} else {
			if err.Error() != StatusCodeZero.Error() {
				errs = errors.Wrapf(err, "ValidateTransactionResult Got Unexpected Error: %v", err)
				ts.logger.Error(errs)
				return
			}
		}

		ts.logger.Debug("Remove From BlackList")

		blackListRemoveHash, err := fCfg.RemoveBlackListAddress(fCfgOwnerKey, blsSrcNet, []string{blsSrcAddr})
		if err != nil {
			errs = errors.Wrapf(err, "RemoveBlackListAddress %v", err)
			ts.logger.Error(errs)
			return
		}
		if _, err := ts.ValidateTransactionResult(ctx, ts.FullConfigAPIChain(), blackListRemoveHash); err != nil {
			errs = errors.Wrapf(err, "ValidateTransactionResult %v", err)
			ts.logger.Error(errs)
			return
		}
		isBlackListed, err = fCfg.IsUserBlackListed(blsSrcNet, blsSrcAddr)
		if err != nil {
			errs = errors.Wrapf(err, "isBlackListed %v", err)
			ts.logger.Error(errs)
			return
		} else if err == nil && isBlackListed {
			errs = fmt.Errorf("Expected addr ( %v , %v ) to not be blacklisted, but was blackListed", blsSrcNet, blsSrcAddr)
			ts.logger.Error(errs)
			return
		}
		if srcChain != ts.FullConfigAPIChain() {
			err = ts.WaitForConfigResponse(ctx, chain.RemoveFromBlacklistRequest, chain.BlacklistResponse, responseChain, blackListRemoveHash,
				map[chain.EventLogType]func(event *evt) error{
					chain.RemoveFromBlacklistRequest: func(ev *evt) error {
						if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
							return errors.New("Got nil value for event ")
						}
						reqEvt, ok := ev.msg.EventLog.(*chain.RemoveFromBlacklistRequestEvent)
						if !ok {
							return fmt.Errorf("Expected *chain.RemoveFromBlacklistRequestEvent. Got %T", ev.msg.EventLog)
						}
						txnRec.feeRecords = append(txnRec.feeRecords, &feeRecord{
							ChainName: ts.FullConfigAPIChain(),
							Sn:        reqEvt.Sn,
							Fee:       map[string]*big.Int{},
						})
						if reqEvt.Net != blsSrcNet {
							return fmt.Errorf("Expected same; Got different reqEvt.Net %v DstNet %v", reqEvt.Net, blsSrcNet)
						}
						if len(reqEvt.Addrs) != 1 {
							return fmt.Errorf("Expected reqEvt.AddrsLen 1; Got %v", len(reqEvt.Addrs))
						}
						if strings.ToLower(reqEvt.Addrs[0]) != strings.ToLower(blsSrcAddr) {
							return fmt.Errorf("Expected same; Got Different reqEvt.Addrs %v DstAddr %v", reqEvt.Addrs[0], dstAddr)
						}
						return nil
					},
					chain.BlacklistResponse: func(ev *evt) error {
						if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
							return errors.New("Got nil value for event ")
						}
						resEvt, ok := ev.msg.EventLog.(*chain.BlacklistResponseEvent)
						if !ok {
							return fmt.Errorf("Expected *chain.BlacklistResponseEvent. Got %T", ev.msg.EventLog)
						}
						if resEvt.Code != 0 {
							return fmt.Errorf("Expected Code 0; Got Sn %v Code %v Msg %v", resEvt.Sn, resEvt.Code, resEvt.Msg)
						}
						return nil
					},
				},
			)
			if err != nil {
				errs = errors.Wrapf(err, "WaitForConfigResponse %v", err)
				ts.logger.Error(errs)
				return
			}
			if isBlackListed, err = stdCfg.IsUserBlackListed(blsSrcNet, blsSrcAddr); err != nil {
				errs = errors.Wrapf(err, "isBlackListed %v", err)
				ts.logger.Error(errs)
				return
			} else if err == nil && isBlackListed {
				errs = fmt.Errorf("Expected addr ( %v , %v ) to not be blacklisted, but was blackListed", blsSrcNet, blsSrcAddr)
				ts.logger.Error(errs)
				return
			}
		}

		ts.logger.Debugf("Sending %v after removing from blacklist \n", blsSrcAddr)

		if errs = ts.Fund(srcChain, srcAddr, userSuppliedAmount, coinName); errs != nil {

			ts.logger.Debug(errors.Wrapf(errs, "srcChain %v, srcAddr %v, coinName %v err %v", srcChain, srcAddr, coinName, errs))
			return
		}
		if errs = ts.Fund(srcChain, srcAddr, gasFeeOnSrc, src.NativeCoin()); errs != nil {

			ts.logger.Debug(errors.Wrapf(errs, "srcChain %v, srcAddr %v, coinName %v err %v", srcChain, srcAddr, coinName, errs))
			return
		}

		if coinName != src.NativeCoin() {
			if approveHash, err = src.Approve(coinName, srcKey, userSuppliedAmount); err != nil {
				errs = errors.Wrapf(err, "Approve Err: %v Hash %v", err, approveHash)
				ts.logger.Error(errs)
				return
			} else {
				if _, err := ts.ValidateTransactionResult(ctx, srcChain, approveHash); err != nil {
					errs = errors.Wrapf(err, "Approve ValidateTransactionResult Err: %v Hash %v", err, approveHash)
					ts.logger.Error(errs)
					return
				}
			}
		}

		transferHashOnSrc, err = src.Transfer(coinName, srcKey, dstAddr, userSuppliedAmount)
		if err != nil {
			errs = errors.Wrapf(err, "Transfer Err: %v", err)
			ts.logger.Error(errs)
			return
		}

		ts.logger.Debug("transferHashOnSrc ", transferHashOnSrc)
		if _, err := ts.ValidateTransactionResult(ctx, srcChain, transferHashOnSrc); err != nil {
			errs = errors.Wrapf(err, "ValidateTransactionResult %v", err)
			ts.logger.Error(errs)
			return
		}
		err = ts.WaitForTransferEvents(ctx, srcChain, dstChain, transferHashOnSrc, map[chain.EventLogType]func(*evt) error{
			chain.TransferStart: func(ev *evt) error {
				if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
					return errors.New("Got nil value for event ")
				}
				startEvt, ok := ev.msg.EventLog.(*chain.TransferStartEvent)
				if !ok {
					return fmt.Errorf("Expected *chain.TransferStartEvent. Got %T", ev.msg.EventLog)
				}
				txnRec.feeRecords = append(txnRec.feeRecords, &feeRecord{
					ChainName: srcChain,
					Sn:        startEvt.Sn,
					Fee:       map[string]*big.Int{startEvt.Assets[0].Name: startEvt.Assets[0].Fee},
				})

				return nil
			},
			chain.TransferEnd: func(ev *evt) error {
				if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
					return errors.New("Got nil value for event ")
				}
				endEvt, ok := ev.msg.EventLog.(*chain.TransferEndEvent)
				if !ok {
					return fmt.Errorf("Expected *chain.TransferEndEvent Got %T", ev.msg.EventLog)
				}

				if endEvt.Code.String() != "0" {
					return fmt.Errorf("Expected code 0 Got %v", endEvt.Code.String())
				}
				return nil
			},
		})
		if err != nil {
			errs = errors.Wrapf(err, "WaitForTransferEvents %v", err)
			ts.logger.Error(errs)
			return
		}
		ts.logger.Debug("pass")
		return
	},
}
var TransferLessThanFee Script = Script{
	Name:        "TransferLessThanFee",
	Description: "Transfer to unknow network",
	Type:        "Transfer",
	Callback: func(ctx context.Context, tp *transferPoint, ts *testSuite) (txnRec *txnRecord, errs error) {
		srcChain := tp.SrcChain
		dstChain := tp.DstChain
		coinNames := tp.CoinNames

		txnRec = &txnRecord{
			feeRecords: []*feeRecord{},
			addresses:  make(map[chain.ChainType][]keypair),
		}
		if len(coinNames) != 1 {
			errs = UnsupportedCoinArgs
			ts.logger.Debug(errs)
			return
		}
		coinName := coinNames[0]
		src, _, err := ts.GetChainPair(srcChain, dstChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetChainPair %v", err)
			ts.logger.Error(errs)
			return
		}

		srcKey, srcAddr, err := ts.GetKeyPairs(srcChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetKeyPairs %v", err)
			ts.logger.Error(errs)
			return
		}
		txnRec.addresses[srcChain] = []keypair{{PrivKey: srcKey, PubKey: srcAddr}}
		dstKey, dstAddr, err := ts.GetKeyPairs(dstChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetKeyPairs %v", err)
			ts.logger.Error(errs)
			return
		}
		txnRec.addresses[dstChain] = []keypair{{PrivKey: dstKey, PubKey: dstAddr}}

		netTransferrableAmount := big.NewInt(-1)
		userSuppliedAmount, err := ts.getAmountBeforeFeeCharge(srcChain, coinName, netTransferrableAmount)
		gasLimitOnSrc := big.NewInt(int64(ts.cfgPerChain[srcChain].GasLimit[chain.TransferCoinInterChainGasLimit]))
		if coinName != src.NativeCoin() {
			gasLimitOnSrc.Add(gasLimitOnSrc, big.NewInt(int64(ts.cfgPerChain[srcChain].GasLimit[chain.ApproveTokenInterChainGasLimit])))
		}
		srcGasPrice := src.SuggestGasPrice()
		gasFeeOnSrc := (&big.Int{}).Mul(srcGasPrice, gasLimitOnSrc)

		if errs = ts.Fund(srcChain, srcAddr, userSuppliedAmount, coinName); errs != nil {

			ts.logger.Debug(errors.Wrapf(errs, "srcChain %v, srcAddr %v, coinName %v err %v", srcChain, srcAddr, coinName, errs))
			return
		}
		if errs = ts.Fund(srcChain, srcAddr, gasFeeOnSrc, src.NativeCoin()); errs != nil {

			ts.logger.Debug(errors.Wrapf(errs, "srcChain %v, srcAddr %v, coinName %v err %v", srcChain, srcAddr, coinName, errs))
			return
		}

		initSrcBalance, err := src.GetCoinBalance(coinName, srcAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		initSrcNativeCoinBalance, err := src.GetCoinBalance(src.NativeCoin(), srcAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}

		// Approve
		var approveHash string
		if coinName != src.NativeCoin() {
			if approveHash, err = src.Approve(coinName, srcKey, userSuppliedAmount); err != nil {
				errs = errors.Wrapf(err, "Approve Err: %v Hash %v", err, approveHash)
				ts.logger.Error(errs)
				return
			} else {
				if _, err := ts.ValidateTransactionResult(ctx, srcChain, approveHash); err != nil {
					errs = errors.Wrapf(err, "Approve ValidateTransactionResult Err: %v Hash %v", err, approveHash)
					ts.logger.Error(errs)
					return
				}
			}
		}

		transferHashOnSrc, err := src.Transfer(coinName, srcKey, dstAddr, userSuppliedAmount)
		if err != nil {
			errs = errors.Wrapf(err, "Transfer Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		ts.logger.Debug("transferHashOnSrc ", transferHashOnSrc)
		if _, errs = ts.ValidateTransactionResult(ctx, srcChain, transferHashOnSrc); errs != nil {
			if errs.Error() == StatusCodeZero.Error() {
				finalSrcBalance, err := src.GetCoinBalance(coinName, srcAddr)
				if err != nil {
					errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
					ts.logger.Error(errs)
					return
				}
				finalSrcNativeCoinBalance, err := src.GetCoinBalance(src.NativeCoin(), srcAddr)
				if err != nil {
					errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
					ts.logger.Error(errs)
					return
				}
				gasSpentOnTxn, err := src.ChargedGasFee(transferHashOnSrc)
				if err != nil {
					errs = errors.Wrapf(err, "GetGasUsed For Transfer Err: %v", err)
					ts.logger.Error(errs)
					return
				}
				if coinName != src.NativeCoin() {
					if initSrcBalance.TotalBalance.Cmp(finalSrcBalance.TotalBalance) != 0 {
						errs = fmt.Errorf("Expected Same, Got Different. finalSrcBalance %v initialSrcBalance %v", finalSrcBalance.TotalBalance, initSrcBalance.TotalBalance)
						ts.logger.Error(errs)
						return
					}
					gasSpentOnApprove, err := src.ChargedGasFee(approveHash)
					if err != nil {
						errs = errors.Wrapf(err, "GetGasUsed for Approve Err: %v", err)
						ts.logger.Error(errs)
						return
					}
					gasSpentOnTxn.Add(gasSpentOnTxn, gasSpentOnApprove)
					tmpDiff := (&big.Int{}).Sub(initSrcNativeCoinBalance.UserBalance, finalSrcNativeCoinBalance.UserBalance)
					if gasSpentOnTxn.Cmp(tmpDiff) != 0 {
						errs = fmt.Errorf("Expected same value; Got different GasSpent %v NativeCoinBalanceDiff %v", gasSpentOnTxn, tmpDiff)
						ts.logger.Error(errs)
						return
					}
				} else {
					tmpDiff := (&big.Int{}).Sub(initSrcNativeCoinBalance.UserBalance, finalSrcNativeCoinBalance.UserBalance)
					if gasSpentOnTxn.Cmp(tmpDiff) != 0 {
						errs = fmt.Errorf("Expected same value; Got different GasSpent %v NativeCoinBalanceDiff %v", gasSpentOnTxn, tmpDiff)
						ts.logger.Error(errs)
						return
					}
				}
				errs = nil
				return
			}
			errs = errors.Wrapf(err, "ValidateTransactionResult Got Unexpected Error: %v", err)
			ts.logger.Error(errs)
			return
		}
		errs = fmt.Errorf("Expected event to fail but it did not ")
		ts.logger.Error(errs)
		return
	},
}
var TransferToBlackListedDstAddress Script = Script{
	Name:        "TransferToBlackListedDstAddress",
	Description: "Transfer to BlackListed Destination Address",
	Type:        "Transfer",
	Callback: func(ctx context.Context, tp *transferPoint, ts *testSuite) (txnRec *txnRecord, errs error) {
		srcChain := tp.SrcChain
		dstChain := tp.DstChain
		coinNames := tp.CoinNames

		txnRec = &txnRecord{
			feeRecords: []*feeRecord{},
			addresses:  make(map[chain.ChainType][]keypair),
		}

		if len(coinNames) != 1 {
			errs = UnsupportedCoinArgs
			ts.logger.Debug(errs)
			return
		}
		coinName := coinNames[0]
		src, _, err := ts.GetChainPair(srcChain, dstChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetChainPair %v", err)
			ts.logger.Error(errs)
			return
		}

		srcKey, srcAddr, err := ts.GetKeyPairs(srcChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetKeyPairs %v", err)
			ts.logger.Error(errs)
			return
		}
		txnRec.addresses[srcChain] = []keypair{{PrivKey: srcKey, PubKey: srcAddr}}
		dstKey, dstAddr, err := ts.GetKeyPairs(dstChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetKeyPairs %v", err)
			ts.logger.Error(errs)
			return
		}
		txnRec.addresses[dstChain] = []keypair{{PrivKey: dstKey, PubKey: dstAddr}}

		netTransferrableAmount := big.NewInt(1)
		userSuppliedAmount, err := ts.getAmountBeforeFeeCharge(srcChain, coinName, netTransferrableAmount)
		gasLimitOnSrc := big.NewInt(int64(ts.cfgPerChain[srcChain].GasLimit[chain.TransferCoinInterChainGasLimit]))
		if coinName != src.NativeCoin() {
			gasLimitOnSrc.Add(gasLimitOnSrc, big.NewInt(int64(ts.cfgPerChain[srcChain].GasLimit[chain.ApproveTokenInterChainGasLimit])))
		}
		gasFeeOnSrc := (&big.Int{}).Mul(src.SuggestGasPrice(), gasLimitOnSrc)

		if errs = ts.Fund(srcChain, srcAddr, userSuppliedAmount, coinName); errs != nil {

			ts.logger.Debug(errors.Wrapf(errs, "srcChain %v, srcAddr %v, coinName %v err %v", srcChain, srcAddr, coinName, errs))
			return
		}
		if errs = ts.Fund(srcChain, srcAddr, gasFeeOnSrc, src.NativeCoin()); errs != nil {

			ts.logger.Debug(errors.Wrapf(errs, "srcChain %v, srcAddr %v, coinName %v err %v", srcChain, srcAddr, coinName, errs))
			return
		}

		// Approve
		var approveHash string
		if coinName != src.NativeCoin() {
			if approveHash, err = src.Approve(coinName, srcKey, userSuppliedAmount); err != nil {
				errs = errors.Wrapf(err, "Approve Err: %v Hash %v", err, approveHash)
				ts.logger.Error(errs)
				return
			} else {
				if _, err := ts.ValidateTransactionResult(ctx, srcChain, approveHash); err != nil {
					errs = errors.Wrapf(err, "Approve ValidateTransactionResult Err: %v Hash %v", err, approveHash)
					ts.logger.Error(errs)
					return
				}
			}
		}

		transferHashOnSrc, err := src.Transfer(coinName, srcKey, dstAddr, userSuppliedAmount)
		if err != nil {
			errs = errors.Wrapf(err, "Transfer Err: %v", err)
			ts.logger.Error(errs)
			return
		}

		ts.logger.Debug("transferHashOnSrc ", transferHashOnSrc)
		if _, err := ts.ValidateTransactionResult(ctx, srcChain, transferHashOnSrc); err != nil {
			errs = errors.Wrapf(err, "ValidateTransactionResult %v", err)
			ts.logger.Error(errs)
			return
		}
		err = ts.WaitForTransferEvents(ctx, srcChain, dstChain, transferHashOnSrc, map[chain.EventLogType]func(*evt) error{
			chain.TransferStart: func(ev *evt) error {
				if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
					return errors.New("Got nil value for event ")
				}
				startEvt, ok := ev.msg.EventLog.(*chain.TransferStartEvent)
				if !ok {
					return fmt.Errorf("Expected *chain.TransferStartEvent. Got %T", ev.msg.EventLog)
				}
				txnRec.feeRecords = append(txnRec.feeRecords, &feeRecord{
					ChainName: srcChain,
					Sn:        startEvt.Sn,
					Fee:       map[string]*big.Int{startEvt.Assets[0].Name: startEvt.Assets[0].Fee},
				})
				return nil
			},
			chain.TransferEnd: func(ev *evt) error {
				if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
					return errors.New("Got nil value for event ")
				}
				endEvt, ok := ev.msg.EventLog.(*chain.TransferEndEvent)
				if !ok {
					return fmt.Errorf("Expected *chain.TransferEndEvent Got %T", ev.msg.EventLog)
				}
				if endEvt.Code.String() != "0" {
					return fmt.Errorf("Expected code 0 Got %v", endEvt.Code.String())
				}
				return nil
			},
		})
		if err != nil {
			errs = errors.Wrapf(err, "WaitForTransferEvents %v", err)
			ts.logger.Error(errs)
			return
		}

		ts.logger.Debug("Start Add to blacklist")

		fCfg, err := ts.GetFullConfigAPI()
		if err != nil {
			errs = errors.Wrapf(err, "GetFullConfigAPI %v", err)
			ts.logger.Error(errs)
			return
		}
		fCfgOwnerKey := ts.FullConfigAPIsOwner()
		stdCfg, err := ts.GetStandardConfigAPI(dstChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetStandardConfigAPI %v", err)
			ts.logger.Error(errs)
			return
		}
		blDstNet, blDstAddr := ts.NetAddr(dstAddr)
		blackListAddHash, err := fCfg.AddBlackListAddress(fCfgOwnerKey, blDstNet, []string{blDstAddr})
		if err != nil {
			errs = errors.Wrapf(err, "AddBlackListAddress %v", err)
			ts.logger.Error(errs)
			return
		}
		if _, err := ts.ValidateTransactionResult(ctx, ts.FullConfigAPIChain(), blackListAddHash); err != nil {
			errs = errors.Wrapf(err, "ValidateTransactionResult %v", err)
			ts.logger.Error(errs)
			return
		}
		isBlackListed, err := fCfg.IsUserBlackListed(blDstNet, blDstAddr)
		if err != nil {
			errs = errors.Wrapf(err, "isBlackListed %v", err)
			ts.logger.Error(errs)
			return
		} else if err == nil && !isBlackListed {
			errs = fmt.Errorf("Expected addr ( %v , %v ) to be blacklisted, but was not", blDstNet, blDstAddr)
			ts.logger.Error(errs)
			return
		}
		responseChain := srcChain
		if responseChain == ts.FullConfigAPIChain() {
			responseChain = dstChain
		}
		if dstChain != ts.FullConfigAPIChain() {
			err = ts.WaitForConfigResponse(ctx, chain.AddToBlacklistRequest, chain.BlacklistResponse, responseChain, blackListAddHash,
				map[chain.EventLogType]func(event *evt) error{
					chain.AddToBlacklistRequest: func(ev *evt) error {
						if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
							return errors.New("Got nil value for event ")
						}
						reqEvt, ok := ev.msg.EventLog.(*chain.AddToBlacklistRequestEvent)
						if !ok {
							return fmt.Errorf("Expected *chain.AddToBlacklistRequestEvent. Got %T", ev.msg.EventLog)
						}
						txnRec.feeRecords = append(txnRec.feeRecords, &feeRecord{
							ChainName: ts.FullConfigAPIChain(),
							Sn:        reqEvt.Sn,
							Fee:       map[string]*big.Int{},
						})
						if reqEvt.Net != blDstNet {
							return fmt.Errorf("Expected same; Got different reqEvt.Net %v DstNet %v", reqEvt.Net, blDstNet)
						}
						if len(reqEvt.Addrs) != 1 {
							return fmt.Errorf("Expected reqEvt.AddrsLen 1; Got %v", len(reqEvt.Addrs))
						}
						if strings.ToLower(reqEvt.Addrs[0]) != strings.ToLower(blDstAddr) {
							return fmt.Errorf("Expected same; Got Different reqEvt.Addrs %v DstAddr %v", reqEvt.Addrs[0], blDstAddr)
						}
						return nil
					},
					chain.BlacklistResponse: func(ev *evt) error {
						if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
							return errors.New("Got nil value for event ")
						}
						resEvt, ok := ev.msg.EventLog.(*chain.BlacklistResponseEvent)
						if !ok {
							return fmt.Errorf("Expected *chain.BlacklistResponseEvent. Got %T", ev.msg.EventLog)
						}
						if resEvt.Code != 0 {
							return fmt.Errorf("Expected Code 0; Got Sn %v Code %v Msg %v", resEvt.Sn, resEvt.Code, resEvt.Msg)
						}
						return nil
					},
				},
			)
			if err != nil {
				errs = errors.Wrapf(err, "WaitForConfigResponse %v", err)
				ts.logger.Error(errs)
				return
			}
			isBlackListed, err = fCfg.IsUserBlackListed(blDstNet, blDstAddr)
			if err != nil {
				errs = errors.Wrapf(err, "isBlackListed %v", err)
				ts.logger.Error(errs)
				return
			} else if err == nil && !isBlackListed {
				errs = fmt.Errorf("Expected addr ( %v , %v ) to be blacklisted, but was not", blDstNet, blDstAddr)
				ts.logger.Error(errs)
				return
			}
		}

		ts.logger.Debug("Send to blacklist")
		if errs = ts.Fund(srcChain, srcAddr, userSuppliedAmount, coinName); errs != nil {

			ts.logger.Debug(errors.Wrapf(errs, "srcChain %v, srcAddr %v, coinName %v err %v", srcChain, srcAddr, coinName, errs))
			return
		}
		if errs = ts.Fund(srcChain, srcAddr, gasFeeOnSrc, src.NativeCoin()); errs != nil {

			ts.logger.Debug(errors.Wrapf(errs, "srcChain %v, srcAddr %v, coinName %v err %v", srcChain, srcAddr, coinName, errs))
			return
		}

		if coinName != src.NativeCoin() {
			if approveHash, err = src.Approve(coinName, srcKey, userSuppliedAmount); err != nil {
				errs = errors.Wrapf(err, "Approve Err: %v Hash %v", err, approveHash)
				ts.logger.Error(errs)
				return
			} else {
				if _, err := ts.ValidateTransactionResult(ctx, srcChain, approveHash); err != nil {
					errs = errors.Wrapf(err, "Approve ValidateTransactionResult Err: %v Hash %v", err, approveHash)
					ts.logger.Error(errs)
					return
				}
			}
		}

		transferHashOnSrc, err = src.Transfer(coinName, srcKey, dstAddr, userSuppliedAmount)
		if err != nil {
			errs = errors.Wrapf(err, "Transfer Err: %v", err)
			ts.logger.Error(errs)
			return
		}

		ts.logger.Debug("transferHashOnSrc ", transferHashOnSrc)
		if _, err := ts.ValidateTransactionResult(ctx, srcChain, transferHashOnSrc); err == nil {

			err = ts.WaitForTransferEvents(ctx, srcChain, dstChain, transferHashOnSrc, map[chain.EventLogType]func(*evt) error{
				chain.TransferStart: func(ev *evt) error {
					if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
						return errors.New("Got nil value for event ")
					}
					startEvt, ok := ev.msg.EventLog.(*chain.TransferStartEvent)
					if !ok {
						return fmt.Errorf("Expected *chain.TransferStartEvent. Got %T", ev.msg.EventLog)
					}
					txnRec.feeRecords = append(txnRec.feeRecords, &feeRecord{
						ChainName: srcChain,
						Sn:        startEvt.Sn,
						Fee:       map[string]*big.Int{startEvt.Assets[0].Name: startEvt.Assets[0].Fee},
					})
					return nil
				},
				chain.TransferEnd: func(ev *evt) error {
					if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
						return errors.New("Got nil value for event ")
					}
					endEvt, ok := ev.msg.EventLog.(*chain.TransferEndEvent)
					if !ok {
						return fmt.Errorf("Expected *chain.TransferEndEvent Got %T", ev.msg.EventLog)
					}
					if endEvt.Code.String() != "1" {
						return fmt.Errorf("Expected error code (1) Got %v", endEvt.Code.String())
					}
					return nil
				},
			})
			if err != nil {
				errs = errors.Wrapf(err, "WaitForTransferEvents %v", err)
				ts.logger.Error(errs)
				return
			}
		} else {
			if err.Error() != StatusCodeZero.Error() {
				errs = errors.Wrapf(err, "ValidateTransactionResult Got Unexpected Error: %v", err)
				ts.logger.Error(errs)
				return
			}
		}

		ts.logger.Debug("Remove From BlackList")

		blackListRemoveHash, err := fCfg.RemoveBlackListAddress(fCfgOwnerKey, blDstNet, []string{blDstAddr})
		if err != nil {
			errs = errors.Wrapf(err, "RemoveBlackListAddress %v", err)
			ts.logger.Error(errs)
			return
		}
		if _, err := ts.ValidateTransactionResult(ctx, ts.FullConfigAPIChain(), blackListRemoveHash); err != nil {
			errs = errors.Wrapf(err, "ValidateTransactionResult %v", err)
			ts.logger.Error(errs)
			return
		}
		isBlackListed, err = fCfg.IsUserBlackListed(blDstNet, blDstAddr)
		if err != nil {
			errs = errors.Wrapf(err, "isBlackListed %v", err)
			ts.logger.Error(errs)
			return
		} else if err == nil && isBlackListed {
			errs = fmt.Errorf("Expected addr ( %v , %v ) to not be blacklisted, but was blackListed", blDstNet, blDstAddr)
			ts.logger.Error(errs)
			return
		}
		if dstChain != ts.FullConfigAPIChain() {
			err = ts.WaitForConfigResponse(ctx, chain.RemoveFromBlacklistRequest, chain.BlacklistResponse, responseChain, blackListRemoveHash,
				map[chain.EventLogType]func(event *evt) error{
					chain.RemoveFromBlacklistRequest: func(ev *evt) error {
						if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
							return errors.New("Got nil value for event ")
						}
						reqEvt, ok := ev.msg.EventLog.(*chain.RemoveFromBlacklistRequestEvent)
						if !ok {
							return fmt.Errorf("Expected *chain.RemoveFromBlacklistRequestEvent. Got %T", ev.msg.EventLog)
						}
						txnRec.feeRecords = append(txnRec.feeRecords, &feeRecord{
							ChainName: ts.FullConfigAPIChain(),
							Sn:        reqEvt.Sn,
							Fee:       map[string]*big.Int{},
						})
						if reqEvt.Net != blDstNet {
							return fmt.Errorf("Expected same; Got different reqEvt.Net %v DstNet %v", reqEvt.Net, blDstNet)
						}
						if len(reqEvt.Addrs) != 1 {
							return fmt.Errorf("Expected reqEvt.AddrsLen 1; Got %v", len(reqEvt.Addrs))
						}
						if strings.ToLower(reqEvt.Addrs[0]) != strings.ToLower(blDstAddr) {
							return fmt.Errorf("Expected same; Got Different reqEvt.Addrs %v DstAddr %v", reqEvt.Addrs[0], dstAddr)
						}
						return nil
					},
					chain.BlacklistResponse: func(ev *evt) error {
						if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
							return errors.New("Got nil value for event ")
						}
						resEvt, ok := ev.msg.EventLog.(*chain.BlacklistResponseEvent)
						if !ok {
							return fmt.Errorf("Expected *chain.BlacklistResponseEvent. Got %T", ev.msg.EventLog)
						}
						if resEvt.Code != 0 {
							return fmt.Errorf("Expected Code 0; Got Sn %v Code %v Msg %v", resEvt.Sn, resEvt.Code, resEvt.Msg)
						}
						return nil
					},
				},
			)
			if err != nil {
				errs = errors.Wrapf(err, "WaitForConfigResponse %v", err)
				ts.logger.Error(errs)
				return
			}
			isBlackListed, err = stdCfg.IsUserBlackListed(blDstNet, blDstAddr)
			if err != nil {
				errs = errors.Wrapf(err, "isBlackListed %v", err)
				ts.logger.Error(errs)
				return
			} else if err == nil && isBlackListed {
				errs = fmt.Errorf("Expected addr ( %v , %v ) to not be blacklisted, but was blackListed", blDstNet, blDstAddr)
				ts.logger.Error(errs)
				return
			}
		}

		if errs = ts.Fund(srcChain, srcAddr, userSuppliedAmount, coinName); errs != nil {

			ts.logger.Debug(errors.Wrapf(errs, "srcChain %v, srcAddr %v, coinName %v err %v", srcChain, srcAddr, coinName, errs))
			return
		}
		if errs = ts.Fund(srcChain, srcAddr, gasFeeOnSrc, src.NativeCoin()); errs != nil {

			ts.logger.Debug(errors.Wrapf(errs, "srcChain %v, srcAddr %v, coinName %v err %v", srcChain, srcAddr, coinName, errs))
			return
		}

		if coinName != src.NativeCoin() {
			if approveHash, err = src.Approve(coinName, srcKey, userSuppliedAmount); err != nil {
				errs = errors.Wrapf(err, "Approve Err: %v Hash %v", err, approveHash)
				ts.logger.Error(errs)
				return
			} else {
				if _, err := ts.ValidateTransactionResult(ctx, srcChain, approveHash); err != nil {
					errs = errors.Wrapf(err, "Approve ValidateTransactionResult Err: %v Hash %v", err, approveHash)
					ts.logger.Error(errs)
					return
				}
			}
		}

		transferHashOnSrc, err = src.Transfer(coinName, srcKey, dstAddr, userSuppliedAmount)
		if err != nil {
			errs = errors.Wrapf(err, "Transfer Err: %v", err)
			ts.logger.Error(errs)
			return
		}

		ts.logger.Debug("transferHashOnSrc ", transferHashOnSrc)
		if _, err := ts.ValidateTransactionResult(ctx, srcChain, transferHashOnSrc); err != nil {
			errs = errors.Wrapf(err, "ValidateTransactionResult %v", err)
			ts.logger.Error(errs)
			return
		}
		err = ts.WaitForTransferEvents(ctx, srcChain, dstChain, transferHashOnSrc, map[chain.EventLogType]func(*evt) error{
			chain.TransferStart: func(ev *evt) error {
				if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
					return errors.New("Got nil value for event ")
				}
				startEvt, ok := ev.msg.EventLog.(*chain.TransferStartEvent)
				if !ok {
					return fmt.Errorf("Expected *chain.TransferStartEvent. Got %T", ev.msg.EventLog)
				}
				txnRec.feeRecords = append(txnRec.feeRecords, &feeRecord{
					ChainName: srcChain,
					Sn:        startEvt.Sn,
					Fee:       map[string]*big.Int{startEvt.Assets[0].Name: startEvt.Assets[0].Fee},
				})

				return nil
			},
			chain.TransferEnd: func(ev *evt) error {
				if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
					return errors.New("Got nil value for event ")
				}
				endEvt, ok := ev.msg.EventLog.(*chain.TransferEndEvent)
				if !ok {
					return fmt.Errorf("Expected *chain.TransferEndEvent Got %T", ev.msg.EventLog)
				}
				if endEvt.Code.String() != "0" {
					return fmt.Errorf("Expected code 0 Got %v", endEvt.Code.String())
				}

				return nil
			},
		})
		if err != nil {
			errs = errors.Wrapf(err, "WaitForTransferEvents %v", err)
			ts.logger.Error(errs)
			return
		}
		ts.logger.Debug("pass")
		return
	},
}
var TransferToUnknownNetwork Script = Script{
	Name:        "TransferToUnknownNetwork",
	Description: "Transfer to unknow network",
	Type:        "Transfer",
	Callback: func(ctx context.Context, tp *transferPoint, ts *testSuite) (txnRec *txnRecord, errs error) {
		srcChain := tp.SrcChain
		dstChain := tp.DstChain
		coinNames := tp.CoinNames

		txnRec = &txnRecord{
			feeRecords: []*feeRecord{},
			addresses:  make(map[chain.ChainType][]keypair),
		}
		if len(coinNames) != 1 {
			errs = UnsupportedCoinArgs
			ts.logger.Debug(errs)
			return
		}
		coinName := coinNames[0]
		src, _, err := ts.GetChainPair(srcChain, dstChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetChainPair %v", err)
			ts.logger.Error(errs)
			return
		}

		srcKey, srcAddr, err := ts.GetKeyPairs(srcChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetKeyPairs %v", err)
			ts.logger.Error(errs)
			return
		}
		txnRec.addresses[srcChain] = []keypair{{PrivKey: srcKey, PubKey: srcAddr}}
		_, tmpDstAddr, err := ts.GetKeyPairs(dstChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetKeyPairs %v", err)
			ts.logger.Error(errs)
			return
		}
		changeBMCNetwork := func(inputStr string) (string, error) {
			splits := strings.Split(inputStr, "/")
			if len(splits) != 4 {
				return "", errors.New("Unexpected length")
			}
			network := splits[2]
			networkSplits := strings.Split(network, ".")
			if len(networkSplits) != 2 {
				return "", errors.New("Unexpected length")
			}
			networkSplits[1] += "s"
			splits[2] = strings.Join(networkSplits, ".")
			return strings.Join(splits, "/"), nil
		}
		dstAddr, err := changeBMCNetwork(tmpDstAddr)

		netTransferrableAmount := big.NewInt(-1)
		userSuppliedAmount, err := ts.getAmountBeforeFeeCharge(srcChain, coinName, netTransferrableAmount)
		gasLimitOnSrc := big.NewInt(int64(ts.cfgPerChain[srcChain].GasLimit[chain.TransferCoinInterChainGasLimit]))
		if coinName != src.NativeCoin() {
			gasLimitOnSrc.Add(gasLimitOnSrc, big.NewInt(int64(ts.cfgPerChain[srcChain].GasLimit[chain.ApproveTokenInterChainGasLimit])))
		}
		srcGasPrice := src.SuggestGasPrice()
		gasFeeOnSrc := (&big.Int{}).Mul(srcGasPrice, gasLimitOnSrc)

		if errs = ts.Fund(srcChain, srcAddr, userSuppliedAmount, coinName); errs != nil {

			ts.logger.Debug(errors.Wrapf(errs, "srcChain %v, srcAddr %v, coinName %v err %v", srcChain, srcAddr, coinName, errs))
			return
		}
		if errs = ts.Fund(srcChain, srcAddr, gasFeeOnSrc, src.NativeCoin()); errs != nil {

			ts.logger.Debug(errors.Wrapf(errs, "srcChain %v, srcAddr %v, coinName %v err %v", srcChain, srcAddr, coinName, errs))
			return
		}

		initSrcBalance, err := src.GetCoinBalance(coinName, srcAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		initSrcNativeCoinBalance, err := src.GetCoinBalance(src.NativeCoin(), srcAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}

		// Approve
		var approveHash string
		if coinName != src.NativeCoin() {
			if approveHash, err = src.Approve(coinName, srcKey, userSuppliedAmount); err != nil {
				errs = errors.Wrapf(err, "Approve Err: %v Hash %v", err, approveHash)
				ts.logger.Error(errs)
				return
			} else {
				if _, err := ts.ValidateTransactionResult(ctx, srcChain, approveHash); err != nil {
					errs = errors.Wrapf(err, "Approve ValidateTransactionResult Err: %v Hash %v", err, approveHash)
					ts.logger.Error(errs)
					return
				}
			}
		}

		transferHashOnSrc, err := src.Transfer(coinName, srcKey, dstAddr, userSuppliedAmount)
		if err != nil {
			errs = errors.Wrapf(err, "Transfer Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		ts.logger.Debug("transferHashOnSrc ", transferHashOnSrc)
		if _, errs = ts.ValidateTransactionResult(ctx, srcChain, transferHashOnSrc); errs != nil {

			if errs.Error() == StatusCodeZero.Error() {
				finalSrcBalance, err := src.GetCoinBalance(coinName, srcAddr)
				if err != nil {
					errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
					ts.logger.Error(errs)
					return
				}
				finalSrcNativeCoinBalance, err := src.GetCoinBalance(src.NativeCoin(), srcAddr)
				if err != nil {
					errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
					ts.logger.Error(errs)
					return
				}
				gasSpentOnTxn, err := src.ChargedGasFee(transferHashOnSrc)
				if err != nil {
					errs = errors.Wrapf(err, "GetGasUsed For Transfer Err: %v", err)
					ts.logger.Error(errs)
					return
				}
				if coinName != src.NativeCoin() {
					if initSrcBalance.TotalBalance.Cmp(finalSrcBalance.TotalBalance) != 0 {
						errs = fmt.Errorf("Expected Same, Got Different. finalSrcBalance %v initialSrcBalance %v", finalSrcBalance.TotalBalance, initSrcBalance.TotalBalance)
						ts.logger.Error(errs)
						return
					}
					gasSpentOnApprove, err := src.ChargedGasFee(approveHash)
					if err != nil {
						errs = errors.Wrapf(err, "GetGasUsed for Approve Err: %v", err)
						ts.logger.Error(errs)
						return
					}
					gasSpentOnTxn.Add(gasSpentOnTxn, gasSpentOnApprove)
					tmpDiff := (&big.Int{}).Sub(initSrcNativeCoinBalance.UserBalance, finalSrcNativeCoinBalance.UserBalance)
					if gasSpentOnTxn.Cmp(tmpDiff) != 0 {
						errs = fmt.Errorf("Expected same value; Got different GasSpent %v NativeCoinBalanceDiff %v", gasSpentOnTxn, tmpDiff)
						ts.logger.Error(errs)
						return
					}
				} else {
					tmpDiff := (&big.Int{}).Sub(initSrcNativeCoinBalance.UserBalance, finalSrcNativeCoinBalance.UserBalance)
					if gasSpentOnTxn.Cmp(tmpDiff) != 0 {
						errs = fmt.Errorf("Expected same value; Got different GasSpent %v NativeCoinBalanceDiff %v", gasSpentOnTxn, tmpDiff)
						ts.logger.Error(errs)
						return
					}
				}
				errs = nil
				return
			}
			errs = errors.Wrapf(err, "ValidateTransactionResult Got Unexpected Error: %v", err)
			ts.logger.Error(errs)
			return
		}
		errs = fmt.Errorf("Expected Transaction to fail but it did not")
		ts.logger.Error(errs)
		return
	},
}
var TransferToZeroAddress Script = Script{
	Name:        "TransferToZeroAddress",
	Description: "Transfer to zero address",
	Type:        "Transfer",
	Callback: func(ctx context.Context, tp *transferPoint, ts *testSuite) (txnRec *txnRecord, errs error) {
		srcChain := tp.SrcChain
		dstChain := tp.DstChain
		coinNames := tp.CoinNames

		txnRec = &txnRecord{
			feeRecords: []*feeRecord{},
			addresses:  make(map[chain.ChainType][]keypair),
		}

		if len(coinNames) != 1 {
			errs = UnsupportedCoinArgs
			ts.logger.Debug(errs)
			return
		}
		coinName := coinNames[0]
		src, dst, err := ts.GetChainPair(srcChain, dstChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetChainPair %v", err)
			ts.logger.Error(errs)
			return
		}

		srcKey, srcAddr, err := ts.GetKeyPairs(srcChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetKeyPairs %v", err)
			ts.logger.Error(errs)
			return
		}
		txnRec.addresses[srcChain] = []keypair{{PrivKey: srcKey, PubKey: srcAddr}}
		_, tmpDstAddr, err := ts.GetKeyPairs(dstChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetKeyPairs %v", err)
			ts.logger.Error(errs)
			return
		}
		convertToZeroAddress := func(inputStr string) string {
			splits := strings.Split(inputStr, "/")
			lenSplits := len(splits)
			if len(splits[lenSplits-1]) > 2 {
				prefix := splits[lenSplits-1][0:2]
				postFix := splits[lenSplits-1][2:]
				byteArr := make([]byte, len(postFix)/2)
				for i := 0; i < len(postFix)/2; i++ {
					byteArr[i] = 0
				}
				splits[lenSplits-1] = prefix + hex.EncodeToString(byteArr)
			}
			return strings.Join(splits, "/")
		}
		dstAddr := convertToZeroAddress(tmpDstAddr)

		netTransferrableAmount := big.NewInt(1)
		userSuppliedAmount, err := ts.getAmountBeforeFeeCharge(srcChain, coinName, netTransferrableAmount)
		gasLimitOnSrc := big.NewInt(int64(ts.cfgPerChain[srcChain].GasLimit[chain.TransferCoinInterChainGasLimit]))
		if coinName != src.NativeCoin() {
			gasLimitOnSrc.Add(gasLimitOnSrc, big.NewInt(int64(ts.cfgPerChain[srcChain].GasLimit[chain.ApproveTokenInterChainGasLimit])))
		}
		gasFeeOnSrc := (&big.Int{}).Mul(src.SuggestGasPrice(), gasLimitOnSrc)

		if errs = ts.Fund(srcChain, srcAddr, userSuppliedAmount, coinName); errs != nil {

			ts.logger.Debug(errors.Wrapf(errs, "srcChain %v, srcAddr %v, coinName %v err %v", srcChain, srcAddr, coinName, errs))
			return
		}
		if errs = ts.Fund(srcChain, srcAddr, gasFeeOnSrc, src.NativeCoin()); errs != nil {

			ts.logger.Debug(errors.Wrapf(errs, "srcChain %v, srcAddr %v, coinName %v err %v", srcChain, srcAddr, coinName, errs))
			return
		}

		initSrcBalance, err := src.GetCoinBalance(coinName, srcAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		initDstBalance, err := dst.GetCoinBalance(coinName, dstAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		initSrcNativeCoinBalance, err := src.GetCoinBalance(src.NativeCoin(), srcAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}

		// Approve
		var approveHash string
		if coinName != src.NativeCoin() {
			if approveHash, err = src.Approve(coinName, srcKey, userSuppliedAmount); err != nil {
				errs = errors.Wrapf(err, "Approve Err: %v Hash %v", err, approveHash)
				ts.logger.Error(errs)
				return
			} else {
				if _, err := ts.ValidateTransactionResult(ctx, srcChain, approveHash); err != nil {
					errs = errors.Wrapf(err, "Approve ValidateTransactionResult Err: %v Hash %v", err, approveHash)
					ts.logger.Error(errs)
					return
				}
			}
		}

		transferHashOnSrc, err := src.Transfer(coinName, srcKey, dstAddr, userSuppliedAmount)
		if err != nil {
			errs = errors.Wrapf(err, "Transfer Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		ts.logger.Debug("transferHashOnSrc ", transferHashOnSrc)
		if _, err := ts.ValidateTransactionResult(ctx, srcChain, transferHashOnSrc); err != nil {
			errs = errors.Wrapf(err, "ValidateTransactionResult %v", err)
			ts.logger.Error(errs)
			return
		}

		errs = ts.WaitForTransferEvents(ctx, srcChain, dstChain, transferHashOnSrc, map[chain.EventLogType]func(*evt) error{
			chain.TransferStart: func(ev *evt) error {
				if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
					return errors.New("Got nil value for event ")
				}
				startEvt, ok := ev.msg.EventLog.(*chain.TransferStartEvent)
				if !ok {
					return fmt.Errorf("Expected *chain.TransferStartEvent. Got %T", ev.msg.EventLog)
				}
				txnRec.feeRecords = append(txnRec.feeRecords, &feeRecord{
					ChainName: srcChain,
					Sn:        startEvt.Sn,
					Fee:       map[string]*big.Int{startEvt.Assets[0].Name: startEvt.Assets[0].Fee},
				})
				startEvt.From = src.GetBTPAddress(startEvt.From)
				if startEvt.From != srcAddr {
					return fmt.Errorf("Expected Same Value for SrcAddr; Got startEvt.From: %v srcAddr: %v", startEvt.From, srcAddr)
				}
				if startEvt.To != dstAddr {
					return fmt.Errorf("Expected Same Value for DstAddr; Got startEvt.To: %v dstAddr: %v", startEvt.To, dstAddr)
				}
				if len(startEvt.Assets) != 1 {
					return fmt.Errorf("For single token transfer; Expected single asset; Got %v", len(startEvt.Assets))
				}
				if startEvt.Assets[0].Name != coinName {
					return fmt.Errorf("Expected same value for coinName; Got startEvt.Assets.Name: %v coinName %v", startEvt.Assets[0].Name, coinName)
				}
				if startEvt.Assets[0].Value.Cmp(netTransferrableAmount) != 0 {
					return fmt.Errorf("Expected same value for coinName; Got startEvt.Value: %v AmountAfterFeeCharge %v", startEvt.Assets[0].Value, netTransferrableAmount)
				}
				sum := (&big.Int{}).Add(startEvt.Assets[0].Value, startEvt.Assets[0].Fee)
				if sum.Cmp(userSuppliedAmount) != 0 {
					return fmt.Errorf("Expected same value for coinName; Got startEvt.Value+Fee: %v AmountBeforeFeeCharge %v", sum, userSuppliedAmount)
				}
				return nil
			},
			chain.TransferEnd: func(ev *evt) error {
				if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
					return errors.New("Got nil value for event ")
				}
				endEvt, ok := ev.msg.EventLog.(*chain.TransferEndEvent)
				if !ok {
					return fmt.Errorf("Expected *chain.TransferEndEvent Got %T", ev.msg.EventLog)
				}
				if endEvt.Code.String() != "1" {
					return fmt.Errorf("Expected error code (1) Got %v", endEvt.Code.String())
				}
				return nil
			},
		})
		if errs != nil {
			errs = errors.Wrapf(err, "WaitForTransferEvents %v", err)
			ts.logger.Error(errs)
			return
		}

		finalSrcBalance, err := src.GetCoinBalance(coinName, srcAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		finalDstBalance, err := dst.GetCoinBalance(coinName, dstAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		finalSrcNativeCoinBalance, err := src.GetCoinBalance(src.NativeCoin(), srcAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		gasSpentOnTxn, err := src.ChargedGasFee(transferHashOnSrc)
		if err != nil {
			errs = errors.Wrapf(err, "GetGasUsed For Transfer Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		if coinName != src.NativeCoin() {
			tmpDiff := (&big.Int{}).Sub(initSrcBalance.UserBalance, finalSrcBalance.UserBalance)
			feeCharged := (&big.Int{}).Sub(userSuppliedAmount, netTransferrableAmount)
			if tmpDiff.Cmp(feeCharged) != 0 {
				errs = fmt.Errorf("Expected same value; Got different feeCharged %v BalanceDiff %v", feeCharged, tmpDiff)
				ts.logger.Error(errs)
				return
			}
			gasSpentOnApprove, err := src.ChargedGasFee(approveHash)
			if err != nil {
				errs = errors.Wrapf(err, "GetGasUsed for Approve Err: %v", err)
				ts.logger.Error(errs)
				return
			}
			gasSpentOnTxn.Add(gasSpentOnTxn, gasSpentOnApprove)
			tmpDiff = (&big.Int{}).Sub(initSrcNativeCoinBalance.UserBalance, finalSrcNativeCoinBalance.UserBalance)
			if gasSpentOnTxn.Cmp(tmpDiff) != 0 {
				errs = fmt.Errorf("Expected same value; Got different GasSpent %v NativeCoinBalanceDiff %v", gasSpentOnTxn, tmpDiff)
				ts.logger.Error(errs)
				return
			}
		} else {
			feeCharged := (&big.Int{}).Sub(userSuppliedAmount, netTransferrableAmount)
			tmpNativeCoinUsed := (&big.Int{}).Add(feeCharged, gasSpentOnTxn)
			tmpDiff := (&big.Int{}).Sub(initSrcBalance.UserBalance, finalSrcBalance.UserBalance)
			if tmpNativeCoinUsed.Cmp(tmpDiff) != 0 {
				errs = fmt.Errorf("Expected same, Got Different. NativeCoinUsed %v SrcDiffAmt %v", tmpNativeCoinUsed, tmpDiff)
				ts.logger.Error(errs)
				return
			}
		}
		if initDstBalance.UserBalance.Cmp(finalDstBalance.UserBalance) != 0 {
			errs = fmt.Errorf("Epected same; Got Different. initDstBalance %v finalDstBalance %v", initDstBalance, finalDstBalance)
			ts.logger.Error(errs)
			return
		}
		return
	},
}
var TransferUniDirection Script = Script{
	Name:        "TransferUniDirection",
	Type:        "Transfer",
	Description: "Transfer Fixed Amount of coin with approve and monitor eventlogs TransferReceived and TransferEnd",
	Callback: func(ctx context.Context, tp *transferPoint, ts *testSuite) (txnRec *txnRecord, errs error) {
		srcChain := tp.SrcChain
		dstChain := tp.DstChain
		coinNames := tp.CoinNames

		txnRec = &txnRecord{
			feeRecords: []*feeRecord{},
			addresses:  make(map[chain.ChainType][]keypair),
		}

		if len(coinNames) != 1 {
			errs = UnsupportedCoinArgs
			ts.logger.Debug(errs)
			return
		}
		coinName := coinNames[0]
		src, tmpOk := ts.clsPerChain[srcChain]
		if !tmpOk {
			errs = fmt.Errorf("Chain %v not found", srcChain)
			ts.logger.Error(errs)
			return
		}
		dst, tmpOk := ts.clsPerChain[dstChain]
		if !tmpOk {
			errs = fmt.Errorf("Chain %v not found", srcChain)
			ts.logger.Error(errs)
			return
		}
		srcKey, srcAddr, err := ts.GetKeyPairs(srcChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetKeyPairs %v", err)
			ts.logger.Error(errs)
			return
		}

		txnRec.addresses[srcChain] = []keypair{{PrivKey: srcKey, PubKey: srcAddr}}
		dstKey, dstAddr, err := ts.GetKeyPairs(dstChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetKeyPairs %v", err)
			ts.logger.Error(errs)
			return
		}
		txnRec.addresses[dstChain] = []keypair{{PrivKey: dstKey, PubKey: dstAddr}}

		ts.logger.Debug("Accounts ", srcAddr, dstAddr)

		tokenAmountAfterFeeChargeOnSrc := big.NewInt(1)
		tokenAmountBeforeFeeChargeOnSrc, err := ts.getAmountBeforeFeeCharge(srcChain, coinName, tokenAmountAfterFeeChargeOnSrc)
		if err != nil {
			errs = errors.Wrapf(err, "getAmountBeforeFeeCharge %v", err)
			ts.logger.Error(errs)
			return
		}

		gasLimitOnSrc := big.NewInt(int64(ts.cfgPerChain[srcChain].GasLimit[chain.TransferCoinInterChainGasLimit]))
		if coinName != src.NativeCoin() {
			gasLimitOnSrc.Add(gasLimitOnSrc, big.NewInt(int64(ts.cfgPerChain[srcChain].GasLimit[chain.ApproveTokenInterChainGasLimit])))
		}
		gasFeeOnSrc := (&big.Int{}).Mul(src.SuggestGasPrice(), gasLimitOnSrc)

		if errs = ts.Fund(srcChain, srcAddr, tokenAmountBeforeFeeChargeOnSrc, coinName); errs != nil {
			ts.logger.Debug(errs)
			return
		}
		if errs = ts.Fund(srcChain, srcAddr, gasFeeOnSrc, src.NativeCoin()); errs != nil {
			ts.logger.Debug(errs)
			return
		}

		initSrcBalance, err := src.GetCoinBalance(coinName, srcAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		initDstBalance, err := dst.GetCoinBalance(coinName, dstAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		initSrcNativeCoinBalance, err := src.GetCoinBalance(src.NativeCoin(), srcAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		ts.logger.Debug("initSrcNativeCoinBalance ", initSrcNativeCoinBalance)
		ts.logger.Debug("initSrcBalance ", initSrcBalance)
		// ApproveToken On Source
		var approveHash string
		if coinName != src.NativeCoin() {
			if approveHash, err = src.Approve(coinName, srcKey, tokenAmountBeforeFeeChargeOnSrc); err != nil {
				errs = errors.Wrapf(err, "Approve Err: %v Hash %v", err, approveHash)
				ts.logger.Error(errs)
				return
			} else {
				if _, err := ts.ValidateTransactionResult(ctx, srcChain, approveHash); err != nil {
					errs = errors.Wrapf(err, "Approve ValidateTransactionResult Err: %v Hash %v", err, approveHash)
					ts.logger.Error(errs)
					return
				}
			}
		}

		transferHashOnSrc, err := src.Transfer(coinName, srcKey, dstAddr, tokenAmountBeforeFeeChargeOnSrc)
		if err != nil {
			errs = errors.Wrapf(err, "Transfer Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		ts.logger.Debug("transferHashOnSrc ", transferHashOnSrc)
		if _, err := ts.ValidateTransactionResult(ctx, srcChain, transferHashOnSrc); err != nil {
			errs = errors.Wrapf(err, "ValidateTransactionResult %v", err)
			ts.logger.Error(errs)
			return
		}

		err = ts.WaitForTransferEvents(ctx, srcChain, dstChain, transferHashOnSrc, map[chain.EventLogType]func(*evt) error{
			chain.TransferStart: func(ev *evt) error {
				if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
					return errors.New("Got nil value for event ")
				}
				startEvt, ok := ev.msg.EventLog.(*chain.TransferStartEvent)
				if !ok {
					return fmt.Errorf("Expected *chain.TransferStartEvent. Got %T", ev.msg.EventLog)
				}
				txnRec.feeRecords = append(txnRec.feeRecords, &feeRecord{
					ChainName: srcChain,
					Sn:        startEvt.Sn,
					Fee:       map[string]*big.Int{startEvt.Assets[0].Name: startEvt.Assets[0].Fee},
				})
				startEvt.From = src.GetBTPAddress(startEvt.From)
				if startEvt.From != srcAddr {
					return fmt.Errorf("Expected Same Value for SrcAddr; Got startEvt.From: %v srcAddr: %v", startEvt.From, srcAddr)
				}
				if startEvt.To != dstAddr {
					return fmt.Errorf("Expected Same Value for DstAddr; Got startEvt.To: %v dstAddr: %v", startEvt.To, dstAddr)
				}
				if len(startEvt.Assets) != 1 {
					return fmt.Errorf("For single token transfer; Expected single asset; Got %v", len(startEvt.Assets))
				}
				if startEvt.Assets[0].Name != coinName {
					return fmt.Errorf("Expected same value for coinName; Got startEvt.Assets.Name: %v coinName %v", startEvt.Assets[0].Name, coinName)
				}
				if startEvt.Assets[0].Value.Cmp(tokenAmountAfterFeeChargeOnSrc) != 0 {
					return fmt.Errorf("Expected same value for coinName; Got startEvt.Value: %v AmountAfterFeeCharge %v", startEvt.Assets[0].Value, tokenAmountAfterFeeChargeOnSrc)
				}
				sum := (&big.Int{}).Add(startEvt.Assets[0].Value, startEvt.Assets[0].Fee)
				if sum.Cmp(tokenAmountBeforeFeeChargeOnSrc) != 0 {
					return fmt.Errorf("Expected same value for coinName; Got startEvt.Value+Fee: %v AmountBeforeFeeCharge %v", sum, tokenAmountBeforeFeeChargeOnSrc)
				}
				return nil
			},
			chain.TransferReceived: func(ev *evt) error {
				if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
					return errors.New("Got nil value for event ")
				}
				receivedEvt, ok := ev.msg.EventLog.(*chain.TransferReceivedEvent)
				if !ok {
					return fmt.Errorf("Expected *chain.TransferReceivedEvent. Got %T", ev.msg.EventLog)
				}
				receivedEvt.To = dst.GetBTPAddress(receivedEvt.To)
				if receivedEvt.To != dstAddr {
					return fmt.Errorf("Expected Same Value for DstAddr; Got receivedEvt.To: %v dstAddr: %v", receivedEvt.To, dstAddr)
				}
				if len(receivedEvt.Assets) != 1 {
					return fmt.Errorf("For single token transfer; Expected single asset; Got %v", len(receivedEvt.Assets))
				}
				if receivedEvt.Assets[0].Name != coinName {
					return fmt.Errorf("Expected same value for coinName; Got receivedEvt.Assets.Name: %v coinName %v", receivedEvt.Assets[0].Name, coinName)
				}
				if receivedEvt.Assets[0].Value.Cmp(tokenAmountAfterFeeChargeOnSrc) != 0 {
					return fmt.Errorf("Expected same value for coinName; Got receivedEvt.Value: %v AmountAfterFeeCharge %v", receivedEvt.Assets[0].Value, tokenAmountAfterFeeChargeOnSrc)
				}
				return nil
			},
			chain.TransferEnd: func(ev *evt) error {
				if ev == nil || (ev != nil && ev.msg == nil) || (ev != nil && ev.msg != nil && ev.msg.EventLog == nil) {
					return errors.New("Got nil value for event ")
				}
				endEvt, ok := ev.msg.EventLog.(*chain.TransferEndEvent)
				if !ok {
					return fmt.Errorf("Expected *chain.TransferEndEvent. Got %T", ev.msg.EventLog)
				}
				if endEvt.Code.String() == "0" {
					ts.logger.Debug("Got Transfer End")
					return nil
				}
				return fmt.Errorf("Unexpected code %v and response %v", endEvt.Code, endEvt.Response)
			},
		})
		if err != nil {
			errs = errors.Wrapf(err, "WaitForTransferEvents %v", err)
			ts.logger.Error(errs)
			return
		}

		finalSrcBalance, err := src.GetCoinBalance(coinName, srcAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		finalSrcNativeCoinBalance, err := src.GetCoinBalance(src.NativeCoin(), srcAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		finalDstBalance, err := dst.GetCoinBalance(coinName, dstAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		gasSpentOnTxn, err := src.ChargedGasFee(transferHashOnSrc)
		if err != nil {
			errs = errors.Wrapf(err, "ChargedGasFee For Transfer Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		if coinName != src.NativeCoin() {
			gasSpentOnApprove, err := src.ChargedGasFee(approveHash)
			if err != nil {
				errs = errors.Wrapf(err, "ChargedGasFee for Approve Err: %v", err)
				ts.logger.Error(errs)
				return
			}
			gasSpentOnTxn.Add(gasSpentOnTxn, gasSpentOnApprove)
			tmpDiff := (&big.Int{}).Sub(initSrcBalance.UserBalance, finalSrcBalance.UserBalance)
			if tokenAmountBeforeFeeChargeOnSrc.Cmp(tmpDiff) != 0 {
				errs = fmt.Errorf("Expected same value for src balance After transfer, Got TransferAmt %v SrcDiffAmt %v", tokenAmountBeforeFeeChargeOnSrc, tmpDiff)
				ts.logger.Error(errs)
				return
			}
			tmpDiff = (&big.Int{}).Sub(initSrcNativeCoinBalance.UserBalance, finalSrcNativeCoinBalance.UserBalance)
			if gasSpentOnTxn.Cmp(tmpDiff) != 0 {
				errs = fmt.Errorf("Expected same value for src nativeCoin balance after txn; Got GasSpentOnTxn %v srcDiffAmt %v", gasSpentOnTxn, tmpDiff)
				ts.logger.Error(errs)
				return
			}
		} else {
			tmpNativeCoinUsed := (&big.Int{}).Add(tokenAmountBeforeFeeChargeOnSrc, gasSpentOnTxn)
			tmpDiff := (&big.Int{}).Sub(initSrcBalance.UserBalance, finalSrcBalance.UserBalance)
			if tmpNativeCoinUsed.Cmp(tmpDiff) != 0 {
				errs = fmt.Errorf("Expected same value for src balance After transfer, Got TransferAmt %v SrcDiffAmt %v", tmpNativeCoinUsed, tmpDiff)
				ts.logger.Error(errs)
				return
			}
		}
		tmpDiff := (&big.Int{}).Sub(finalDstBalance.UserBalance, initDstBalance.UserBalance)
		if tokenAmountAfterFeeChargeOnSrc.Cmp(tmpDiff) != 0 {
			errs = fmt.Errorf("Expected same value for dst balance After transfer, Got TransferAmt %v DstDiffAmt %v", tokenAmountAfterFeeChargeOnSrc, tmpDiff)
			ts.logger.Error(errs)
			return
		}
		return
	},
}
var TransferWithoutApprove Script = Script{
	Name:        "TransferWithoutApprove",
	Description: "Transfer Without Approve",
	Type:        "Transfer",
	Callback: func(ctx context.Context, tp *transferPoint, ts *testSuite) (txnRec *txnRecord, errs error) {
		srcChain := tp.SrcChain
		dstChain := tp.DstChain
		coinNames := tp.CoinNames

		txnRec = &txnRecord{
			feeRecords: []*feeRecord{},
			addresses:  make(map[chain.ChainType][]keypair),
		}
		if len(coinNames) != 1 {
			errs = UnsupportedCoinArgs
			ts.logger.Debugf(" Should specify a single coinName, got %v", len(coinNames))
			return
		}
		coinName := coinNames[0]

		src, _, err := ts.GetChainPair(srcChain, dstChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetChainPair %v", err)
			ts.logger.Error(errs)
			return
		}
		if coinName == src.NativeCoin() {
			ts.logger.Debugf("Expected non-native coin; Got native coin %v", src.NativeCoin())
			errs = UnsupportedCoinArgs
			return
		}

		srcKey, srcAddr, err := ts.GetKeyPairs(srcChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetKeyPairs %v", err)
			ts.logger.Error(errs)
			return
		}
		txnRec.addresses[srcChain] = []keypair{{PrivKey: srcKey, PubKey: srcAddr}}
		dstKey, dstAddr, err := ts.GetKeyPairs(dstChain)
		if err != nil {
			errs = errors.Wrapf(err, "GetKeyPairs %v", err)
			ts.logger.Error(errs)
			return
		}
		txnRec.addresses[dstChain] = []keypair{{PrivKey: dstKey, PubKey: dstAddr}}

		netTransferrableAmount := big.NewInt(1)
		userSuppliedAmount, err := ts.getAmountBeforeFeeCharge(srcChain, coinName, netTransferrableAmount)
		gasLimitOnSrc := big.NewInt(int64(ts.cfgPerChain[srcChain].GasLimit[chain.TransferCoinInterChainGasLimit]))
		srcGasPrice := src.SuggestGasPrice()
		gasFeeOnSrc := (&big.Int{}).Mul(srcGasPrice, gasLimitOnSrc)

		if errs = ts.Fund(srcChain, srcAddr, userSuppliedAmount, coinName); errs != nil {

			ts.logger.Debug(errors.Wrapf(errs, "srcChain %v, srcAddr %v, coinName %v err %v", srcChain, srcAddr, coinName, errs))
			return
		}
		if errs = ts.Fund(srcChain, srcAddr, gasFeeOnSrc, src.NativeCoin()); errs != nil {

			ts.logger.Debug(errors.Wrapf(errs, "srcChain %v, srcAddr %v, coinName %v err %v", srcChain, srcAddr, coinName, errs))
			return
		}

		initSrcBalance, err := src.GetCoinBalance(coinName, srcAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		initSrcNativeCoinBalance, err := src.GetCoinBalance(src.NativeCoin(), srcAddr)
		if err != nil {
			errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
			ts.logger.Error(errs)
			return
		}

		transferHashOnSrc, err := src.Transfer(coinName, srcKey, dstAddr, userSuppliedAmount)
		if err != nil {
			errs = errors.Wrapf(err, "Transfer Err: %v", err)
			ts.logger.Error(errs)
			return
		}
		ts.logger.Debug("transferHashOnSrc ", transferHashOnSrc)

		if _, errs = ts.ValidateTransactionResult(ctx, srcChain, transferHashOnSrc); errs != nil {
			if errs.Error() == StatusCodeZero.Error() {

				finalSrcBalance, err := src.GetCoinBalance(coinName, srcAddr)
				if err != nil {
					errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
					ts.logger.Error(errs)
					return
				}
				finalSrcNativeCoinBalance, err := src.GetCoinBalance(src.NativeCoin(), srcAddr)
				if err != nil {
					errs = errors.Wrapf(err, "GetCoinBalance Err: %v", err)
					ts.logger.Error(errs)
					return
				}
				gasSpentOnTxn, err := src.ChargedGasFee(transferHashOnSrc)
				if err != nil {
					errs = errors.Wrapf(err, "GetGasUsed For Transfer Err: %v", err)
					ts.logger.Error(errs)
					return
				}
				if initSrcBalance.UserBalance.Cmp(finalSrcBalance.UserBalance) != 0 {
					errs = fmt.Errorf("Expected same value; Got different initSrcBalance %v finalSrcbalance %v", initSrcBalance.UserBalance, finalSrcBalance.UserBalance)
					ts.logger.Error(errs)
					return
				}
				tmpDiff := (&big.Int{}).Sub(initSrcNativeCoinBalance.UserBalance, finalSrcNativeCoinBalance.UserBalance)
				if gasSpentOnTxn.Cmp(tmpDiff) != 0 {
					errs = fmt.Errorf("Expected same value; Got different GasSpent %v NativeCoinBalanceDiff %v", gasSpentOnTxn, tmpDiff)
					ts.logger.Error(errs)
					return
				}
				errs = nil
				return
			}
			errs = errors.Wrapf(err, "ValidateTransactionResult Got Unexpected Error: %v", err)
			ts.logger.Error(errs)
			return
		}
		errs = fmt.Errorf("Expected event to fail but it did not ")
		ts.logger.Error(errs)
		return
	},
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL