Repository: xuperchain/xuperunion
Pull request #450 (2019-10-28 11:26:34)

contract: add Transfer interface to contract
Add Transfer interface to contract.


Patch:
diff --git a/cmd/cli/comm_trans.go b/cmd/cli/comm_trans.go
index 942c9e2..5339466 100755
--- a/cmd/cli/comm_trans.go
+++ b/cmd/cli/comm_trans.go
@@ -52,6 +52,9 @@ type CommTrans struct {
 	Keys         string
 	XchainClient pb.XchainClient
 	CryptoType   string
+
+	// DebugTx if enabled, tx will be printed instead of being posted
+	DebugTx bool
 }
 
 // GenerateTx generate raw tx
@@ -79,12 +82,17 @@ func (c *CommTrans) GenPreExeRes(ctx context.Context) (
 				Args:       c.Args,
 			})
 		} else {
-			preExeReqs = append(preExeReqs, &pb.InvokeRequest{
+			invokeReq := &pb.InvokeRequest{
 				ModuleName:   c.ModuleName,
 				ContractName: c.ContractName,
 				MethodName:   c.MethodName,
 				Args:         c.Args,
-			})
+			}
+			// transfer to contract
+			if c.To == c.ContractName {
+				invokeReq.Amount = c.Amount
+			}
+			preExeReqs = append(preExeReqs, invokeReq)
 		}
 	} else {
 		tmpReq, err := c.GetInvokeRequestFromDesc()
@@ -190,18 +198,26 @@ func (c *CommTrans) GenRawTx(ctx context.Context, desc []byte, preExeRes *pb.Inv
 		gasUsed = preExeRes.GasUsed
 		fmt.Printf("The gas you cousume is: %v\n", gasUsed)
 	}
+
+	if preExeRes.GetUtxoInputs() != nil {
+		tx.TxInputs = append(tx.TxInputs, preExeRes.GetUtxoInputs()...)
+	}
+	if preExeRes.GetUtxoOutputs() != nil {
+		tx.TxOutputs = append(tx.TxOutputs, preExeRes.GetUtxoOutputs()...)
+	}
+
 	txOutputs, totalNeed, err := c.GenTxOutputs(gasUsed)
 	if err != nil {
 		return nil, err
 	}
-	tx.TxOutputs = txOutputs
+	tx.TxOutputs = append(tx.TxOutputs, txOutputs...)
 
 	txInputs, deltaTxOutput, err := c.GenTxInputs(ctx, totalNeed)
 	if err != nil {
 		return nil, err
 	}
 
-	tx.TxInputs = txInputs
+	tx.TxInputs = append(tx.TxInputs, txInputs...)
 	if deltaTxOutput != nil {
 		tx.TxOutputs = append(tx.TxOutputs, deltaTxOutput)
 	}
@@ -359,6 +375,12 @@ func (c *CommTrans) Transfer(ctx context.Context) error {
 	if err != nil {
 		return err
 	}
+	if c.DebugTx {
+		ttx := FromPBTx(tx)
+		out, _ := json.MarshalIndent(ttx, "", "  ")
+		fmt.Println(string(out))
+		return nil
+	}
 
 	return c.SendTx(ctx, tx)
 }
diff --git a/cmd/cli/wasm_invoke.go b/cmd/cli/wasm_invoke.go
index a2431dd..d84e1d4 100755
--- a/cmd/cli/wasm_invoke.go
+++ b/cmd/cli/wasm_invoke.go
@@ -25,6 +25,8 @@ type WasmInvokeCommand struct {
 	multiAddrs string
 	output     string
 	methodName string
+	amount     string
+	debug      bool
 }
 
 // NewWasmInvokeCommand new wasm invoke cmd
@@ -53,6 +55,8 @@ func (c *WasmInvokeCommand) addFlags() {
 	c.cmd.Flags().StringVarP(&c.multiAddrs, "multiAddrs", "A", "data/acl/addrs", "multiAddrs if multisig scene")
 	c.cmd.Flags().StringVarP(&c.output, "output", "o", "./tx.out", "tx draw data")
 	c.cmd.Flags().StringVarP(&c.methodName, "method", "", "invoke", "contract method name")
+	c.cmd.Flags().StringVarP(&c.amount, "amount", "", "", "the amount transfer to contract")
+	c.cmd.Flags().BoolVarP(&c.debug, "debug", "", false, "debug print tx instead of posting")
 
 }
 
@@ -79,6 +83,12 @@ func (c *WasmInvokeCommand) invoke(ctx context.Context, codeName string) error {
 		Keys:         c.cli.RootOptions.Keys,
 		XchainClient: c.cli.XchainClient(),
 		CryptoType:   c.cli.RootOptions.CryptoType,
+		DebugTx:      c.debug,
+	}
+	// transfer to contract
+	if c.amount != "" {
+		ct.To = ct.ContractName
+		ct.Amount = c.amount
 	}
 
 	// generate preExe params
diff --git a/contract/bridge/adaptor.go b/contract/bridge/adaptor.go
index 427084b..a3521cd 100644
--- a/contract/bridge/adaptor.go
+++ b/contract/bridge/adaptor.go
@@ -90,6 +90,7 @@ func (v *vmImpl) NewContext(ctxCfg *contract.ContextConfig) (contract.Context, e
 	ctx.ResourceLimits = ctxCfg.ResourceLimits
 	ctx.CanInitialize = ctxCfg.CanInitialize
 	ctx.Core = ctxCfg.Core
+	ctx.TransferAmount = ctxCfg.TransferAmount
 	release := func() {
 		v.ctxmgr.DestroyContext(ctx)
 	}
@@ -98,6 +99,7 @@ func (v *vmImpl) NewContext(ctxCfg *contract.ContextConfig) (contract.Context, e
 		v.ctxmgr.DestroyContext(ctx)
 		return nil, err
 	}
+	ctx.Instance = instance
 	return &vmContextImpl{
 		ctx:      ctx,
 		instance: instance,
diff --git a/contract/bridge/context.go b/contract/bridge/context.go
index 47ca180..24a1bb1 100644
--- a/contract/bridge/context.go
+++ b/contract/bridge/context.go
@@ -31,6 +31,10 @@ type Context struct {
 
 	Core contract.ChainCore
 
+	TransferAmount string
+
+	Instance Instance
+
 	// Write by contract
 	Output *pb.Response
 }
diff --git a/contract/bridge/syscall_service.go b/contract/bridge/syscall_service.go
index 66311e9..fe72fbc 100644
--- a/contract/bridge/syscall_service.go
+++ b/contract/bridge/syscall_service.go
@@ -109,6 +109,21 @@ func (c *SyscallService) QueryTx(ctx context.Context, in *pb.QueryTxRequest) (*p
 
 // Transfer implements Syscall interface
 func (c *SyscallService) Transfer(ctx context.Context, in *pb.TransferRequest) (*pb.TransferResponse, error) {
+	nctx, ok := c.ctxmgr.Context(in.GetHeader().Ctxid)
+	if !ok {
+		return nil, fmt.Errorf("bad ctx id:%d", in.Header.Ctxid)
+	}
+	amount, ok := new(big.Int).SetString(in.GetAmount(), 10)
+	if !ok {
+		return nil, errors.New("parse amount error")
+	}
+	if in.GetTo() == "" {
+		return nil, errors.New("empty to address")
+	}
+	err := nctx.Cache.Transfer(nctx.ContractName, in.GetTo(), amount)
+	if err != nil {
+		return nil, err
+	}
 	resp := &pb.TransferResponse{}
 	return resp, nil
 }
@@ -135,6 +150,7 @@ func (c *SyscallService) PutObject(ctx context.Context, in *pb.PutRequest) (*pb.
 	}
 
 	if nctx.ExceedDiskLimit() {
+		nctx.Instance.Abort(ErrOutOfDiskLimit.Error())
 		return nil, ErrOutOfDiskLimit
 	}
 	return &pb.PutResponse{}, nil
@@ -217,10 +233,11 @@ func (c *SyscallService) GetCallArgs(ctx context.Context, in *pb.GetCallArgsRequ
 		return args[i].Key < args[j].Key
 	})
 	return &pb.CallArgs{
-		Method:      nctx.Method,
-		Args:        args,
-		Initiator:   nctx.Initiator,
-		AuthRequire: nctx.AuthRequire,
+		Method:         nctx.Method,
+		Args:           args,
+		Initiator:      nctx.Initiator,
+		AuthRequire:    nctx.AuthRequire,
+		TransferAmount: nctx.TransferAmount,
 	}, nil
 }
 
diff --git a/contract/bridge/xbridge.go b/contract/bridge/xbridge.go
index cc399d2..d3c57a1 100644
--- a/contract/bridge/xbridge.go
+++ b/contract/bridge/xbridge.go
@@ -21,6 +21,8 @@ type Instance interface {
 	ResourceUsed() contract.Limits
 	// Release releases contract instance
 	Release()
+	// Abort terminates running contract with error message
+	Abort(msg string)
 }
 
 // XBridge 用于注册用户虚拟机以及向Xchain Core注册可被识别的vm.VirtualMachine
diff --git a/contract/kernel/acl.go b/contract/kernel/acl.go
index 0d126d2..a5303b5 100644
--- a/contract/kernel/acl.go
+++ b/contract/kernel/acl.go
@@ -80,7 +80,7 @@ func (na *NewAccountMethod) Invoke(ctx *KContext, args map[string][]byte) (*cont
 		return nil, validErr
 	}
 
-	bcname := ctx.ModelCache.GetBcname()
+	bcname := ctx.ContextConfig.BCName
 	if bcname == "" {
 		return nil, fmt.Errorf("block name is empty")
 	}
diff --git a/contract/native/xuper3.go b/contract/native/xuper3.go
index b94270e..257367a 100644
--- a/contract/native/xuper3.go
+++ b/contract/native/xuper3.go
@@ -86,3 +86,6 @@ func (n *nativeInstance) Exec() error {
 
 	return nil
 }
+
+func (n *nativeInstance) Abort(msg string) {
+}
diff --git a/contract/vm.go b/contract/vm.go
index 07cc882..0fd62dc 100644
--- a/contract/vm.go
+++ b/contract/vm.go
@@ -57,7 +57,15 @@ type ContextConfig struct {
 	ResourceLimits           Limits
 	// Whether contract can be initialized
 	CanInitialize bool
-	Core          ChainCore
+
+	// The chain service
+	Core ChainCore
+
+	// The amount transfer to contract
+	TransferAmount string
+
+	// Chain name
+	BCName string
 }
 
 // VirtualMachine define virtual machine interface
diff --git a/contract/wasm/bridge_instance.go b/contract/wasm/bridge_instance.go
index 0d61258..416d0b6 100644
--- a/contract/wasm/bridge_instance.go
+++ b/contract/wasm/bridge_instance.go
@@ -45,3 +45,7 @@ func (v *bridgeInstance) ResourceUsed() contract.Limits {
 func (v *bridgeInstance) Release() {
 	v.vmInstance.Release()
 }
+
+func (v *bridgeInstance) Abort(msg string) {
+	v.vmInstance.Abort(msg)
+}
diff --git a/contract/wasm/vm/memory/creator.go b/contract/wasm/vm/memory/creator.go
index 9c714ee..c2f89eb 100644
--- a/contract/wasm/vm/memory/creator.go
+++ b/contract/wasm/vm/memory/creator.go
@@ -80,6 +80,9 @@ func (m *memoryInstance) ResourceUsed() contract.Limits {
 func (m *memoryInstance) Release() {
 }
 
+func (m *memoryInstance) Abort(msg string) {
+}
+
 func init() {
 	vm.Register("memory", newMemoryInstanceCreator)
 }
diff --git a/contract/wasm/vm/vm.go b/contract/wasm/vm/vm.go
index 4496cb1..1dffeb0 100644
--- a/contract/wasm/vm/vm.go
+++ b/contract/wasm/vm/vm.go
@@ -37,4 +37,5 @@ type Instance interface {
 	Exec(function string) error
 	ResourceUsed() contract.Limits
 	Release()
+	Abort(msg string)
 }
diff --git a/contract/wasm/vm/xvm/creator.go b/contract/wasm/vm/xvm/creator.go
index d72d4ac..4bbf3f6 100644
--- a/contract/wasm/vm/xvm/creator.go
+++ b/contract/wasm/vm/xvm/creator.go
@@ -182,6 +182,10 @@ func (x *xvmInstance) Release() {
 	x.execCtx.Release()
 }
 
+func (x *xvmInstance) Abort(msg string) {
+	exec.Throw(exec.NewTrap(msg))
+}
+
 func (x *xvmInstance) InitDebugWriter(logger *log.Logger) {
 	if logger == nil {
 		return
diff --git a/contractsdk/cpp/example/builtin_types.cc b/contractsdk/cpp/example/builtin_types.cc
index caf0073..8bfae02 100644
--- a/contractsdk/cpp/example/builtin_types.cc
+++ b/contractsdk/cpp/example/builtin_types.cc
@@ -45,7 +45,7 @@ DEFINE_METHOD(BuiltinTypes, transfer) {
     const std::string& amount = ctx->arg("amount");
     xchain::Account account = ctx->sender();
     printf("Account name: %s\n", account.get_name().c_str());
-    if (account.transfer(to, amount)) {
+    if (account.transfer(amount)) {
         ctx->ok("Transfer success");
     } else {
         ctx->error("Transfer failed");
diff --git a/contractsdk/cpp/xchain/account.cc b/contractsdk/cpp/xchain/account.cc
index 88a9e98..19fc434 100644
--- a/contractsdk/cpp/xchain/account.cc
+++ b/contractsdk/cpp/xchain/account.cc
@@ -14,11 +14,11 @@ Account::~Account() {}
 
 const std::string& Account::get_name() { return _name; }
 
-bool Account::transfer(const std::string& to, const std::string& amount) {
+bool Account::transfer(const std::string& amount) {
     pb::TransferRequest req;
     pb::TransferResponse rep;
-    req.set_from(_name);
-    req.set_to(to);
+    // req.set_from(_name);
+    req.set_to(_name);
     req.set_amount(amount);
     return syscall("Transfer", req, &rep);
 }
diff --git a/contractsdk/cpp/xchain/account.h b/contractsdk/cpp/xchain/account.h
index 62438e2..1e855ff 100644
--- a/contractsdk/cpp/xchain/account.h
+++ b/contractsdk/cpp/xchain/account.h
@@ -19,7 +19,7 @@ public:
     const std::string& get_name();
     AccountType type() const;
     bool get_addresses(std::vector<std::string>* out);
-    bool transfer(const std::string& to, const std::string& amount);
+    bool transfer(const std::string& amount);
 
 private:
     std::string _name;
diff --git a/contractsdk/cpp/xchain/context_impl.cc b/contractsdk/cpp/xchain/context_impl.cc
index 368cfc3..e6649d5 100644
--- a/contractsdk/cpp/xchain/context_impl.cc
+++ b/contractsdk/cpp/xchain/context_impl.cc
@@ -137,4 +137,8 @@ std::unique_ptr<Iterator> ContextImpl::new_iterator(const std::string& start, co
 
 Account& ContextImpl::sender() { return _account; }
 
+const std::string& ContextImpl::transfer_amount() const {
+    return _call_args.transfer_amount();
+}
+
 }  // namespace xchain
diff --git a/contractsdk/cpp/xchain/context_impl.h b/contractsdk/cpp/xchain/context_impl.h
index 522ef26..a05dfaf 100644
--- a/contractsdk/cpp/xchain/context_impl.h
+++ b/contractsdk/cpp/xchain/context_impl.h
@@ -30,6 +30,7 @@ public:
     virtual Response* mutable_response();
     virtual std::unique_ptr<Iterator> new_iterator(const std::string& start, const std::string& limit);
     virtual Account& sender();
+    virtual const std::string& transfer_amount() const;
 
 private:
     pb::CallArgs _call_args;
diff --git a/contractsdk/cpp/xchain/contract.pb.cc b/contractsdk/cpp/xchain/contract.pb.cc
index 078f585..77a302b 100644
--- a/contractsdk/cpp/xchain/contract.pb.cc
+++ b/contractsdk/cpp/xchain/contract.pb.cc
@@ -1742,6 +1742,7 @@ const int CallArgs::kMethodFieldNumber;
 const int CallArgs::kArgsFieldNumber;
 const int CallArgs::kInitiatorFieldNumber;
 const int CallArgs::kAuthRequireFieldNumber;
+const int CallArgs::kTransferAmountFieldNumber;
 #endif  // !defined(_MSC_VER) || _MSC_VER >= 1900
 
 CallArgs::CallArgs()
@@ -1763,6 +1764,10 @@ CallArgs::CallArgs(const CallArgs& from)
   if (from.initiator().size() > 0) {
     initiator_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.initiator_);
   }
+  transfer_amount_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+  if (from.transfer_amount().size() > 0) {
+    transfer_amount_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.transfer_amount_);
+  }
   // @@protoc_insertion_point(copy_constructor:xchain.contract.sdk.CallArgs)
 }
 
@@ -1771,6 +1776,7 @@ void CallArgs::SharedCtor() {
       &scc_info_CallArgs_contract_2eproto.base);
   method_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
   initiator_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+  transfer_amount_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
 
 CallArgs::~CallArgs() {
@@ -1781,6 +1787,7 @@ CallArgs::~CallArgs() {
 void CallArgs::SharedDtor() {
   method_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
   initiator_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+  transfer_amount_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
 }
 
 void CallArgs::SetCachedSize(int size) const {
@@ -1802,6 +1809,7 @@ void CallArgs::Clear() {
   auth_require_.Clear();
   method_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
   initiator_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+  transfer_amount_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
   _internal_metadata_.Clear();
 }
 
@@ -1885,6 +1893,22 @@ const char* CallArgs::_InternalParse(const char* begin, const char* end, void* o
         } while ((::google::protobuf::io::UnalignedLoad<::google::protobuf::uint64>(ptr) & 255) == 34 && (ptr += 1));
         break;
       }
+      // string transfer_amount = 5;
+      case 5: {
+        if (static_cast<::google::protobuf::uint8>(tag) != 42) goto handle_unusual;
+        ptr = ::google::protobuf::io::ReadSize(ptr, &size);
+        GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
+        ctx->extra_parse_data().SetFieldName(nullptr);
+        object = msg->mutable_transfer_amount();
+        if (size > end - ptr + ::google::protobuf::internal::ParseContext::kSlopBytes) {
+          parser_till_end = ::google::protobuf::internal::GreedyStringParserUTF8;
+          goto string_till_end;
+        }
+        GOOGLE_PROTOBUF_PARSER_ASSERT(::google::protobuf::internal::StringCheckUTF8(ptr, size, ctx));
+        ::google::protobuf::internal::InlineGreedyStringParser(object, ptr, size, ctx);
+        ptr += size;
+        break;
+      }
       default: {
       handle_unusual:
         if ((tag & 7) == 4 || tag == 0) {
@@ -1982,6 +2006,21 @@ bool CallArgs::MergePartialFromCodedStream(
         break;
       }
 
+      // string transfer_amount = 5;
+      case 5: {
+        if (static_cast< ::google::protobuf::uint8>(tag) == (42 & 0xFF)) {
+          DO_(::google::protobuf::internal::WireFormatLite::ReadString(
+                input, this->mutable_transfer_amount()));
+          DO_(::google::protobuf::internal::WireFormatLite::VerifyUtf8String(
+            this->transfer_amount().data(), static_cast<int>(this->transfer_amount().length()),
+            ::google::protobuf::internal::WireFormatLite::PARSE,
+            "xchain.contract.sdk.CallArgs.transfer_amount"));
+        } else {
+          goto handle_unusual;
+        }
+        break;
+      }
+
       default: {
       handle_unusual:
         if (tag == 0) {
@@ -2048,6 +2087,16 @@ void CallArgs::SerializeWithCachedSizes(
       4, this->auth_require(i), output);
   }
 
+  // string transfer_amount = 5;
+  if (this->transfer_amount().size() > 0) {
+    ::google::protobuf::internal::WireFormatLite::VerifyUtf8String(
+      this->transfer_amount().data(), static_cast<int>(this->transfer_amount().length()),
+      ::google::protobuf::internal::WireFormatLite::SERIALIZE,
+      "xchain.contract.sdk.CallArgs.transfer_amount");
+    ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased(
+      5, this->transfer_amount(), output);
+  }
+
   output->WriteRaw(_internal_metadata_.unknown_fields().data(),
                    static_cast<int>(_internal_metadata_.unknown_fields().size()));
   // @@protoc_insertion_point(serialize_end:xchain.contract.sdk.CallArgs)
@@ -2096,6 +2145,13 @@ size_t CallArgs::ByteSizeLong() const {
         this->initiator());
   }
 
+  // string transfer_amount = 5;
+  if (this->transfer_amount().size() > 0) {
+    total_size += 1 +
+      ::google::protobuf::internal::WireFormatLite::StringSize(
+        this->transfer_amount());
+  }
+
   int cached_size = ::google::protobuf::internal::ToCachedSize(total_size);
   SetCachedSize(cached_size);
   return total_size;
@@ -2123,6 +2179,10 @@ void CallArgs::MergeFrom(const CallArgs& from) {
 
     initiator_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.initiator_);
   }
+  if (from.transfer_amount().size() > 0) {
+
+    transfer_amount_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.transfer_amount_);
+  }
 }
 
 void CallArgs::CopyFrom(const CallArgs& from) {
@@ -2149,6 +2209,8 @@ void CallArgs::InternalSwap(CallArgs* other) {
     GetArenaNoVirtual());
   initiator_.Swap(&other->initiator_, &::google::protobuf::internal::GetEmptyStringAlreadyInited(),
     GetArenaNoVirtual());
+  transfer_amount_.Swap(&other->transfer_amount_, &::google::protobuf::internal::GetEmptyStringAlreadyInited(),
+    GetArenaNoVirtual());
 }
 
 ::std::string CallArgs::GetTypeName() const {
diff --git a/contractsdk/cpp/xchain/contract.pb.h b/contractsdk/cpp/xchain/contract.pb.h
index f5708f0..697d626 100644
--- a/contractsdk/cpp/xchain/contract.pb.h
+++ b/contractsdk/cpp/xchain/contract.pb.h
@@ -893,6 +893,20 @@ class CallArgs :
   ::std::string* release_initiator();
   void set_allocated_initiator(::std::string* initiator);
 
+  // string transfer_amount = 5;
+  void clear_transfer_amount();
+  static const int kTransferAmountFieldNumber = 5;
+  const ::std::string& transfer_amount() const;
+  void set_transfer_amount(const ::std::string& value);
+  #if LANG_CXX11
+  void set_transfer_amount(::std::string&& value);
+  #endif
+  void set_transfer_amount(const char* value);
+  void set_transfer_amount(const char* value, size_t size);
+  ::std::string* mutable_transfer_amount();
+  ::std::string* release_transfer_amount();
+  void set_allocated_transfer_amount(::std::string* transfer_amount);
+
   // @@protoc_insertion_point(class_scope:xchain.contract.sdk.CallArgs)
  private:
   class HasBitSetters;
@@ -902,6 +916,7 @@ class CallArgs :
   ::google::protobuf::RepeatedPtrField<::std::string> auth_require_;
   ::google::protobuf::internal::ArenaStringPtr method_;
   ::google::protobuf::internal::ArenaStringPtr initiator_;
+  ::google::protobuf::internal::ArenaStringPtr transfer_amount_;
   mutable ::google::protobuf::internal::CachedSize _cached_size_;
   friend struct ::TableStruct_contract_2eproto;
 };
@@ -4948,6 +4963,59 @@ CallArgs::mutable_auth_require() {
   return &auth_require_;
 }
 
+// string transfer_amount = 5;
+inline void CallArgs::clear_transfer_amount() {
+  transfer_amount_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline const ::std::string& CallArgs::transfer_amount() const {
+  // @@protoc_insertion_point(field_get:xchain.contract.sdk.CallArgs.transfer_amount)
+  return transfer_amount_.GetNoArena();
+}
+inline void CallArgs::set_transfer_amount(const ::std::string& value) {
+  
+  transfer_amount_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+  // @@protoc_insertion_point(field_set:xchain.contract.sdk.CallArgs.transfer_amount)
+}
+#if LANG_CXX11
+inline void CallArgs::set_transfer_amount(::std::string&& value) {
+  
+  transfer_amount_.SetNoArena(
+    &::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::move(value));
+  // @@protoc_insertion_point(field_set_rvalue:xchain.contract.sdk.CallArgs.transfer_amount)
+}
+#endif
+inline void CallArgs::set_transfer_amount(const char* value) {
+  GOOGLE_DCHECK(value != nullptr);
+  
+  transfer_amount_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+  // @@protoc_insertion_point(field_set_char:xchain.contract.sdk.CallArgs.transfer_amount)
+}
+inline void CallArgs::set_transfer_amount(const char* value, size_t size) {
+  
+  transfer_amount_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
+      ::std::string(reinterpret_cast<const char*>(value), size));
+  // @@protoc_insertion_point(field_set_pointer:xchain.contract.sdk.CallArgs.transfer_amount)
+}
+inline ::std::string* CallArgs::mutable_transfer_amount() {
+  
+  // @@protoc_insertion_point(field_mutable:xchain.contract.sdk.CallArgs.transfer_amount)
+  return transfer_amount_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline ::std::string* CallArgs::release_transfer_amount() {
+  // @@protoc_insertion_point(field_release:xchain.contract.sdk.CallArgs.transfer_amount)
+  
+  return transfer_amount_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline void CallArgs::set_allocated_transfer_amount(::std::string* transfer_amount) {
+  if (transfer_amount != nullptr) {
+    
+  } else {
+    
+  }
+  transfer_amount_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), transfer_amount);
+  // @@protoc_insertion_point(field_set_allocated:xchain.contract.sdk.CallArgs.transfer_amount)
+}
+
 // -------------------------------------------------------------------
 
 // SyscallHeader
diff --git a/contractsdk/cpp/xchain/xchain.h b/contractsdk/cpp/xchain/xchain.h
index 69d3a46..b45172c 100644
--- a/contractsdk/cpp/xchain/xchain.h
+++ b/contractsdk/cpp/xchain/xchain.h
@@ -39,6 +39,7 @@ public:
     virtual Response* mutable_response() = 0;
     virtual std::unique_ptr<Iterator> new_iterator(const std::string& start, const std::string& limit) = 0;
     virtual Account& sender() = 0;
+    virtual const std::string& transfer_amount() const = 0;
 };
 
 class Contract {
diff --git a/contractsdk/go/pb/contract.pb.go b/contractsdk/go/pb/contract.pb.go
index 2d17515..bd87b4a 100644
--- a/contractsdk/go/pb/contract.pb.go
+++ b/contractsdk/go/pb/contract.pb.go
@@ -205,6 +205,7 @@ type CallArgs struct {
 	Args                 []*ArgPair `protobuf:"bytes,2,rep,name=args,proto3" json:"args,omitempty"`
 	Initiator            string     `protobuf:"bytes,3,opt,name=initiator,proto3" json:"initiator,omitempty"`
 	AuthRequire          []string   `protobuf:"bytes,4,rep,name=auth_require,json=authRequire,proto3" json:"auth_require,omitempty"`
+	TransferAmount       string     `protobuf:"bytes,5,opt,name=transfer_amount,json=transferAmount,proto3" json:"transfer_amount,omitempty"`
 	XXX_NoUnkeyedLiteral struct{}   `json:"-"`
 	XXX_unrecognized     []byte     `json:"-"`
 	XXX_sizecache        int32      `json:"-"`
@@ -263,6 +264,13 @@ func (m *CallArgs) GetAuthRequire() []string {
 	return nil
 }
 
+func (m *CallArgs) GetTransferAmount() string {
+	if m != nil {
+		return m.TransferAmount
+	}
+	return ""
+}
+
 type SyscallHeader struct {
 	Ctxid                int64    `protobuf:"varint,1,opt,name=ctxid,proto3" json:"ctxid,omitempty"`
 	XXX_NoUnkeyedLiteral struct{} `json:"-"`
@@ -1734,74 +1742,76 @@ func init() {
 func init() { proto.RegisterFile("contract.proto", fileDescriptor_d19debeba7dea55a) }
 
 var fileDescriptor_d19debeba7dea55a = []byte{
-	// 1101 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0x4b, 0x6f, 0xdb, 0x46,
-	0x10, 0x06, 0xf5, 0xe6, 0x48, 0x96, 0x6d, 0xda, 0x48, 0xe9, 0x47, 0x00, 0x85, 0x41, 0x01, 0xf5,
-	0x62, 0xa7, 0x2e, 0x90, 0x22, 0x6d, 0x2f, 0x8e, 0x8b, 0xda, 0x46, 0x81, 0xc6, 0x61, 0x74, 0x0a,
-	0x50, 0x28, 0x2b, 0x72, 0x25, 0x2d, 0x2c, 0x71, 0x99, 0xdd, 0x61, 0x40, 0xf7, 0xde, 0x43, 0xd0,
-	0x73, 0x8f, 0xfd, 0x4d, 0xfd, 0x4b, 0xc5, 0x3e, 0x28, 0x51, 0x88, 0x9c, 0xb6, 0x88, 0x72, 0x9b,
-	0x19, 0xed, 0xce, 0x7c, 0x33, 0xfb, 0xf1, 0xdb, 0x15, 0x74, 0x23, 0x9e, 0xa0, 0x20, 0x11, 0x9e,
-	0xa4, 0x82, 0x23, 0xf7, 0xf6, 0xf2, 0x68, 0x4a, 0x58, 0x72, 0xb2, 0x08, 0xcb, 0xf8, 0x36, 0xd8,
-	0x82, 0xf6, 0x0d, 0x4b, 0x26, 0x21, 0x7d, 0x9b, 0x51, 0x89, 0x41, 0x17, 0x3a, 0xc6, 0x95, 0x29,
-	0x4f, 0x24, 0x0d, 0xbe, 0x82, 0xdd, 0x5f, 0x08, 0xb2, 0x77, 0xf4, 0x82, 0xcc, 0x66, 0x76, 0x91,
-	0xb7, 0x0f, 0xf5, 0x08, 0x73, 0x16, 0xfb, 0x4e, 0xcf, 0xe9, 0x57, 0x43, 0xe3, 0x04, 0xfb, 0xe0,
-	0x95, 0x97, 0xda, 0x04, 0x5f, 0x43, 0xf3, 0x5c, 0x4c, 0x6e, 0x08, 0x13, 0xde, 0x0e, 0x54, 0x6f,
-	0xe9, 0x9d, 0xde, 0xe4, 0x86, 0xca, 0x54, 0x89, 0xde, 0x91, 0x59, 0x46, 0xfd, 0x4a, 0xcf, 0xe9,
-	0x77, 0x42, 0xe3, 0x04, 0x7f, 0x3a, 0xd0, 0x52, 0x39, 0xce, 0xc5, 0x44, 0x7a, 0x0f, 0xa0, 0x31,
-	0xa7, 0x38, 0xe5, 0xb1, 0xdd, 0x67, 0x3d, 0xef, 0x09, 0xd4, 0x88, 0x98, 0x48, 0xbf, 0xd2, 0xab,
-	0xf6, 0xdb, 0x67, 0xc7, 0x27, 0x6b, 0x7a, 0x3b, 0xb1, 0x85, 0x43, 0xbd, 0xd2, 0x3b, 0x06, 0x97,
-	0x25, 0x0c, 0x19, 0x41, 0x2e, 0xfc, 0xaa, 0x4e, 0xb6, 0x0c, 0x78, 0x8f, 0xa0, 0x43, 0x32, 0x9c,
-	0x0e, 0x05, 0x7d, 0x9b, 0x31, 0x41, 0xfd, 0x5a, 0xaf, 0xda, 0x77, 0xc3, 0xb6, 0x8a, 0x85, 0x26,
-	0x14, 0x7c, 0x09, 0x5b, 0xaf, 0xee, 0x64, 0x44, 0x66, 0xb3, 0x2b, 0x4a, 0x62, 0x2a, 0xee, 0x99,
-	0x43, 0x0a, 0x70, 0x93, 0x61, 0x31, 0xab, 0xef, 0xa0, 0x31, 0xd5, 0xab, 0xf5, 0xa2, 0xf6, 0x59,
-	0xb0, 0x16, 0xe9, 0x4a, 0xde, 0xd0, 0xee, 0x28, 0x06, 0x66, 0x86, 0xb3, 0x3a, 0xb0, 0x6a, 0x79,
-	0x60, 0xea, 0x0c, 0x55, 0x45, 0x3b, 0xf2, 0xd7, 0x00, 0x97, 0xf4, 0xf3, 0x00, 0x08, 0x1e, 0x43,
-	0x5b, 0xe7, 0x36, 0xa5, 0x96, 0x78, 0x9c, 0x32, 0x9e, 0x5f, 0x61, 0xeb, 0x47, 0x3a, 0xa3, 0x48,
-	0x3f, 0x0f, 0x86, 0x1d, 0xe8, 0x16, 0xe9, 0x6d, 0xc7, 0x7f, 0x38, 0xb0, 0x7d, 0x8d, 0x54, 0xa8,
-	0x93, 0xdc, 0x44, 0xcd, 0x7d, 0xa8, 0x4b, 0x24, 0x02, 0x0b, 0x5e, 0x6a, 0x47, 0x45, 0x67, 0x6c,
-	0xce, 0xb0, 0x18, 0xbe, 0x76, 0x14, 0xbe, 0x88, 0xa4, 0x7e, 0xad, 0xe7, 0xf4, 0xeb, 0xa1, 0x32,
-	0x83, 0xa7, 0xd0, 0x29, 0xc0, 0x5c, 0x23, 0x9d, 0x97, 0x79, 0xdf, 0xf9, 0x18, 0xef, 0x7f, 0x86,
-	0x9d, 0x65, 0x13, 0x76, 0xc0, 0xdf, 0x42, 0x9d, 0x21, 0x9d, 0x4b, 0xdf, 0xd1, 0x3c, 0x7f, 0xb4,
-	0xb6, 0x89, 0x72, 0xb5, 0xd0, 0xac, 0x0f, 0xde, 0x40, 0xf7, 0x65, 0x46, 0xc5, 0xdd, 0x20, 0xdf,
-	0xc4, 0x40, 0x3c, 0xa8, 0x69, 0xa2, 0x57, 0xf4, 0x67, 0xa3, 0xed, 0xe0, 0x02, 0xb6, 0x17, 0x15,
-	0x2c, 0xda, 0x27, 0x50, 0xc1, 0xdc, 0xa6, 0xef, 0xad, 0x4d, 0x3f, 0x10, 0x24, 0x91, 0x24, 0x42,
-	0xc6, 0x93, 0xb0, 0x82, 0x79, 0xc0, 0x60, 0x57, 0x27, 0x79, 0x3e, 0xe3, 0xd1, 0xed, 0x26, 0x90,
-	0xfa, 0xd0, 0x1c, 0xa9, 0x5c, 0x0b, 0xb0, 0x85, 0x1b, 0xfc, 0x04, 0x5e, 0xb9, 0xd4, 0x02, 0x72,
-	0x5d, 0x2f, 0xb0, 0xa5, 0x0e, 0xd7, 0x96, 0x32, 0x5b, 0xcc, 0xc2, 0xe0, 0xbd, 0x03, 0xdb, 0xba,
-	0x8d, 0x31, 0x15, 0x1b, 0x9a, 0xed, 0x58, 0xf0, 0x79, 0x31, 0x5b, 0x65, 0x7b, 0x5d, 0xa8, 0x20,
-	0xb7, 0x22, 0x55, 0x41, 0xae, 0x54, 0x90, 0xcc, 0x79, 0x96, 0xa0, 0xe6, 0x99, 0x1b, 0x5a, 0x2f,
-	0xf0, 0x60, 0x67, 0x09, 0xc5, 0x7e, 0x0c, 0x7f, 0x3b, 0xb0, 0x77, 0x61, 0xcb, 0x96, 0x55, 0xfb,
-	0x53, 0x30, 0x2a, 0x15, 0xe6, 0x71, 0x36, 0xa3, 0x16, 0xa5, 0xf5, 0xbc, 0x43, 0x68, 0x15, 0xbb,
-	0x2d, 0xda, 0x85, 0x5f, 0x52, 0xee, 0xda, 0x5a, 0xe5, 0xae, 0xff, 0x57, 0xe5, 0x0e, 0x5e, 0xc2,
-	0xfe, 0x6a, 0x43, 0xf6, 0xec, 0x9e, 0x41, 0x4b, 0x58, 0xdb, 0xf6, 0xf4, 0x70, 0x6d, 0xb6, 0x62,
-	0x43, 0xb8, 0x58, 0x1e, 0xdc, 0x40, 0x6b, 0x91, 0xe6, 0x01, 0x34, 0x24, 0x12, 0xcc, 0xa4, 0x4e,
-	0x52, 0x0f, 0xad, 0xa7, 0xa8, 0x34, 0xa7, 0x52, 0x92, 0x49, 0xd1, 0x75, 0xe1, 0xaa, 0x23, 0x1b,
-	0xf1, 0xf8, 0xce, 0x0a, 0x81, 0xb6, 0x15, 0x2d, 0x76, 0x5e, 0x51, 0x7c, 0x91, 0x61, 0xba, 0x19,
-	0xf5, 0x2f, 0x77, 0x57, 0xf9, 0x7f, 0xdd, 0xed, 0xc1, 0x6e, 0x09, 0xca, 0xa2, 0x65, 0xef, 0x92,
-	0x62, 0x71, 0xb1, 0x6e, 0x00, 0x61, 0xf0, 0x97, 0x03, 0xcd, 0x41, 0x7e, 0x9d, 0xa4, 0x19, 0x7a,
-	0x07, 0x0a, 0xed, 0x78, 0xb8, 0xb8, 0x0e, 0xdd, 0xb0, 0x29, 0xe8, 0x78, 0x90, 0xb3, 0xd8, 0x7b,
-	0x08, 0xa0, 0x7e, 0xe2, 0xe3, 0xb1, 0xa4, 0x46, 0x52, 0xeb, 0xa1, 0x2b, 0xe8, 0xf8, 0x85, 0x0e,
-	0x78, 0x47, 0xe0, 0x2a, 0xce, 0x0f, 0x49, 0x1c, 0x0b, 0xbf, 0xae, 0x27, 0xda, 0x52, 0x81, 0xf3,
-	0x38, 0x16, 0x25, 0xe2, 0x37, 0xca, 0xc4, 0xf7, 0x1e, 0xc3, 0xd6, 0x58, 0xf0, 0xdf, 0x68, 0x32,
-	0x9c, 0x52, 0x36, 0x99, 0xa2, 0xdf, 0xd4, 0x57, 0x70, 0xc7, 0x04, 0xaf, 0x74, 0x2c, 0x78, 0x03,
-	0xad, 0x41, 0x6e, 0xa6, 0x50, 0x4a, 0xe4, 0xac, 0x24, 0xfa, 0x02, 0x9a, 0xc8, 0x4d, 0x6d, 0x23,
-	0xc6, 0x0d, 0xe4, 0xba, 0xf2, 0x07, 0x15, 0x6a, 0x6b, 0x2a, 0xbc, 0xaf, 0x40, 0xbb, 0x24, 0x69,
-	0x0b, 0x9d, 0x74, 0x96, 0x3a, 0x79, 0xbf, 0x22, 0x79, 0xcf, 0xc0, 0xc5, 0x7c, 0xc8, 0xd4, 0xfc,
-	0xa4, 0x5f, 0xfd, 0xc8, 0xe7, 0x60, 0x87, 0x1c, 0xb6, 0xd0, 0x18, 0xd2, 0xfb, 0x01, 0x00, 0xf3,
-	0x21, 0xd7, 0xbd, 0x49, 0xfd, 0x58, 0xb9, 0x8f, 0x1e, 0xc5, 0x04, 0x42, 0x17, 0xad, 0x25, 0x15,
-	0xcc, 0x98, 0xca, 0x48, 0xcf, 0xb4, 0x13, 0x6a, 0x7b, 0xf5, 0x79, 0x74, 0xf8, 0x6f, 0xcf, 0xa3,
-	0xa3, 0x0f, 0x9f, 0x47, 0xbf, 0x57, 0xa0, 0xae, 0x85, 0xb2, 0xdc, 0x71, 0x75, 0xb5, 0xe3, 0x03,
-	0x68, 0xa5, 0x82, 0x0e, 0xa7, 0x44, 0x4e, 0xad, 0x2a, 0x34, 0x53, 0x41, 0xaf, 0x88, 0x9c, 0x2a,
-	0x29, 0x49, 0x05, 0x4f, 0xb9, 0xa4, 0x0b, 0x16, 0x14, 0xbe, 0xc2, 0x2b, 0xd9, 0x24, 0xb1, 0x1c,
-	0xd0, 0xb6, 0x3a, 0xd0, 0x34, 0x1b, 0xa9, 0x8b, 0xb5, 0x69, 0xce, 0xcd, 0x78, 0x2a, 0x6e, 0x0f,
-	0xcc, 0xd5, 0x07, 0x66, 0x3d, 0x75, 0xe7, 0xaa, 0xe3, 0x90, 0x7e, 0x5b, 0x43, 0x37, 0x8e, 0x02,
-	0x84, 0xf9, 0x30, 0xd2, 0xc4, 0xe8, 0x68, 0x66, 0x36, 0x31, 0xbf, 0xd0, 0xcc, 0x38, 0x80, 0x16,
-	0x4b, 0x86, 0x28, 0xb2, 0xe4, 0xd6, 0xef, 0xf6, 0x9c, 0x7e, 0x2b, 0x6c, 0xb2, 0x64, 0xa0, 0x5c,
-	0x45, 0xd9, 0x84, 0xe6, 0x68, 0xfa, 0xd8, 0x36, 0xba, 0xa7, 0x02, 0xaa, 0x91, 0x40, 0xc0, 0xe1,
-	0x25, 0xc5, 0xf3, 0x48, 0x27, 0x55, 0x54, 0xa2, 0x52, 0x52, 0xb9, 0xa1, 0xbb, 0x8d, 0x98, 0xb4,
-	0x05, 0x93, 0xac, 0x1b, 0x7c, 0x0f, 0x47, 0x6b, 0x6b, 0x5a, 0x85, 0x3b, 0x06, 0x97, 0x14, 0x41,
-	0xfd, 0x92, 0x70, 0xc3, 0x65, 0xe0, 0xf9, 0xd3, 0xab, 0xea, 0xeb, 0xb3, 0x09, 0xc3, 0x69, 0x36,
-	0x3a, 0x89, 0xf8, 0xfc, 0x34, 0xcf, 0x52, 0x2a, 0x34, 0x28, 0x63, 0x66, 0x09, 0xe3, 0xc9, 0x69,
-	0x81, 0x4f, 0xc6, 0xb7, 0xa7, 0x13, 0x7e, 0x9a, 0x8e, 0x46, 0x0d, 0xfd, 0xb7, 0xe2, 0x9b, 0x7f,
-	0x02, 0x00, 0x00, 0xff, 0xff, 0x47, 0x8e, 0x2c, 0x3d, 0x68, 0x0c, 0x00, 0x00,
+	// 1123 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0xcd, 0x6e, 0xdb, 0x46,
+	0x10, 0x06, 0xf5, 0xaf, 0x91, 0x2c, 0xdb, 0xb4, 0x91, 0xd2, 0x3f, 0x01, 0x14, 0x06, 0x45, 0xd5,
+	0x8b, 0x9d, 0xba, 0x40, 0x8a, 0xb4, 0xbd, 0x38, 0x2e, 0x6a, 0x1b, 0x05, 0x1a, 0x87, 0xd1, 0x29,
+	0x40, 0xa1, 0xac, 0xc8, 0x95, 0xb4, 0xb0, 0xc4, 0x65, 0x76, 0x87, 0x01, 0xdd, 0x7b, 0x0f, 0x41,
+	0x9f, 0xa1, 0xaf, 0xd2, 0x57, 0xe8, 0x2b, 0x15, 0xfb, 0x43, 0x8a, 0x42, 0xe4, 0xb4, 0x45, 0x95,
+	0xdb, 0xcc, 0x68, 0xf7, 0x9b, 0x6f, 0x66, 0x67, 0xbf, 0xa5, 0xa0, 0x17, 0xf2, 0x18, 0x05, 0x09,
+	0xf1, 0x24, 0x11, 0x1c, 0xb9, 0xbb, 0x97, 0x85, 0x33, 0xc2, 0xe2, 0x93, 0x22, 0x2c, 0xa3, 0x5b,
+	0x7f, 0x0b, 0x3a, 0x37, 0x2c, 0x9e, 0x06, 0xf4, 0x6d, 0x4a, 0x25, 0xfa, 0x3d, 0xe8, 0x1a, 0x57,
+	0x26, 0x3c, 0x96, 0xd4, 0xff, 0x12, 0x76, 0x7f, 0x26, 0xc8, 0xde, 0xd1, 0x0b, 0x32, 0x9f, 0xdb,
+	0x45, 0xee, 0x3e, 0xd4, 0x43, 0xcc, 0x58, 0xe4, 0x39, 0x7d, 0x67, 0x50, 0x0d, 0x8c, 0xe3, 0xef,
+	0x83, 0x5b, 0x5e, 0x6a, 0x01, 0xbe, 0x82, 0xe6, 0xb9, 0x98, 0xde, 0x10, 0x26, 0xdc, 0x1d, 0xa8,
+	0xde, 0xd2, 0x3b, 0xbd, 0xa9, 0x1d, 0x28, 0x53, 0x01, 0xbd, 0x23, 0xf3, 0x94, 0x7a, 0x95, 0xbe,
+	0x33, 0xe8, 0x06, 0xc6, 0xf1, 0xff, 0x74, 0xa0, 0xa5, 0x30, 0xce, 0xc5, 0x54, 0xba, 0x0f, 0xa0,
+	0xb1, 0xa0, 0x38, 0xe3, 0x91, 0xdd, 0x67, 0x3d, 0xf7, 0x09, 0xd4, 0x88, 0x98, 0x4a, 0xaf, 0xd2,
+	0xaf, 0x0e, 0x3a, 0x67, 0xc7, 0x27, 0x6b, 0x6a, 0x3b, 0xb1, 0x89, 0x03, 0xbd, 0xd2, 0x3d, 0x86,
+	0x36, 0x8b, 0x19, 0x32, 0x82, 0x5c, 0x78, 0x55, 0x0d, 0xb6, 0x0c, 0xb8, 0x8f, 0xa0, 0x4b, 0x52,
+	0x9c, 0x8d, 0x04, 0x7d, 0x9b, 0x32, 0x41, 0xbd, 0x5a, 0xbf, 0x3a, 0x68, 0x07, 0x1d, 0x15, 0x0b,
+	0x4c, 0xc8, 0xfd, 0x02, 0xb6, 0x51, 0x90, 0x58, 0x4e, 0xa8, 0x18, 0x91, 0x05, 0x4f, 0x63, 0xf4,
+	0xea, 0x1a, 0xa6, 0x97, 0x87, 0xcf, 0x75, 0xd4, 0xff, 0x1c, 0xb6, 0x5e, 0xdd, 0xc9, 0x90, 0xcc,
+	0xe7, 0x57, 0x94, 0x44, 0x54, 0xdc, 0xd3, 0xb0, 0x04, 0xe0, 0x26, 0xc5, 0xbc, 0xa9, 0xdf, 0x42,
+	0x63, 0xa6, 0x57, 0xeb, 0x45, 0x9d, 0x33, 0x7f, 0x6d, 0x49, 0x2b, 0xb8, 0x81, 0xdd, 0x91, 0x77,
+	0xd6, 0x74, 0x71, 0xb5, 0xb3, 0xd5, 0x72, 0x67, 0xd5, 0x61, 0xab, 0x8c, 0xf6, 0x6c, 0x5e, 0x03,
+	0x5c, 0xd2, 0x4f, 0x43, 0xc0, 0x7f, 0x0c, 0x1d, 0x8d, 0x6d, 0x52, 0x2d, 0xf9, 0x38, 0x65, 0x3e,
+	0xbf, 0xc0, 0xd6, 0x0f, 0x74, 0x4e, 0x91, 0x7e, 0x1a, 0x0e, 0x3b, 0xd0, 0xcb, 0xe1, 0x6d, 0xc5,
+	0xbf, 0x3b, 0xb0, 0x7d, 0x8d, 0x54, 0xa8, 0x23, 0xdf, 0x44, 0xce, 0x7d, 0xa8, 0x4b, 0x24, 0x02,
+	0xf3, 0x01, 0xd6, 0x8e, 0x8a, 0xce, 0xd9, 0x82, 0x61, 0xde, 0x7c, 0xed, 0x28, 0x7e, 0x21, 0x49,
+	0xbc, 0x5a, 0xdf, 0x19, 0xd4, 0x03, 0x65, 0xfa, 0x4f, 0xa1, 0x9b, 0x93, 0xb9, 0x46, 0xba, 0x28,
+	0x5f, 0x90, 0xee, 0xc7, 0x2e, 0xc8, 0x4f, 0xb0, 0xb3, 0x2c, 0xc2, 0x36, 0xf8, 0x1b, 0xa8, 0x33,
+	0xa4, 0x0b, 0xe9, 0x39, 0xfa, 0x42, 0x3c, 0x5a, 0x5b, 0x44, 0x39, 0x5b, 0x60, 0xd6, 0xfb, 0x6f,
+	0xa0, 0xf7, 0x32, 0xa5, 0xe2, 0x6e, 0x98, 0x6d, 0xa2, 0x21, 0x2e, 0xd4, 0xf4, 0xa0, 0x57, 0xf4,
+	0xc5, 0xd0, 0xb6, 0x7f, 0x01, 0xdb, 0x45, 0x06, 0xcb, 0xf6, 0x09, 0x54, 0x30, 0xb3, 0xf0, 0xfd,
+	0xb5, 0xf0, 0x43, 0x75, 0xa5, 0x48, 0x88, 0x8c, 0xc7, 0x41, 0x05, 0x33, 0x9f, 0xc1, 0xae, 0x06,
+	0x79, 0x3e, 0xe7, 0xe1, 0xed, 0x26, 0x98, 0x7a, 0xd0, 0x1c, 0x2b, 0xac, 0x82, 0x6c, 0xee, 0xfa,
+	0x3f, 0x82, 0x5b, 0x4e, 0x55, 0x50, 0xae, 0xeb, 0x05, 0x36, 0xd5, 0xe1, 0xda, 0x54, 0x66, 0x8b,
+	0x59, 0xe8, 0xbf, 0x77, 0x60, 0x7b, 0x68, 0x95, 0x61, 0x43, 0xbd, 0x9d, 0x08, 0xbe, 0xc8, 0x7b,
+	0xab, 0x6c, 0xb7, 0x07, 0x15, 0xe4, 0x56, 0xcd, 0x2a, 0xc8, 0x95, 0x5c, 0x5a, 0x69, 0xaa, 0x19,
+	0xb9, 0x34, 0x9e, 0xef, 0xc2, 0xce, 0x92, 0x8a, 0xbd, 0x0c, 0x7f, 0x39, 0xb0, 0x77, 0x61, 0xd3,
+	0x96, 0xe5, 0xfd, 0xff, 0x70, 0x54, 0x72, 0xcd, 0xa3, 0x74, 0x4e, 0x2d, 0x4b, 0xeb, 0xb9, 0x87,
+	0xd0, 0xca, 0x77, 0x5b, 0xb6, 0x85, 0x5f, 0x92, 0xf8, 0xda, 0x5a, 0x89, 0xaf, 0xff, 0x5b, 0x89,
+	0xf7, 0x5f, 0xc2, 0xfe, 0x6a, 0x41, 0xf6, 0xec, 0x9e, 0x41, 0x4b, 0x58, 0xdb, 0xd6, 0xf4, 0x70,
+	0x2d, 0x5a, 0xbe, 0x21, 0x28, 0x96, 0xfb, 0x37, 0xd0, 0x2a, 0x60, 0x1e, 0x40, 0x43, 0x22, 0xc1,
+	0x54, 0x6a, 0x90, 0x7a, 0x60, 0x3d, 0x35, 0x4a, 0x0b, 0x2a, 0x25, 0x99, 0xe6, 0x55, 0xe7, 0xae,
+	0x3a, 0xb2, 0x31, 0x8f, 0xee, 0xac, 0x10, 0x68, 0x5b, 0x8d, 0xc5, 0xce, 0x2b, 0x8a, 0x2f, 0x52,
+	0x4c, 0x36, 0xa3, 0xfe, 0xe5, 0xea, 0x2a, 0xff, 0xad, 0xba, 0x3d, 0xd8, 0x2d, 0x51, 0x29, 0x4a,
+	0x76, 0x2f, 0x29, 0xe6, 0x2f, 0xf0, 0x06, 0x18, 0xfa, 0x7f, 0x38, 0xd0, 0x1c, 0x66, 0xd7, 0x71,
+	0x92, 0xa2, 0x7b, 0xa0, 0xd8, 0x4e, 0x46, 0xc5, 0x73, 0xd8, 0x0e, 0x9a, 0x82, 0x4e, 0x86, 0x19,
+	0x8b, 0xdc, 0x87, 0x00, 0xea, 0x27, 0x3e, 0x99, 0x48, 0x6a, 0x24, 0xb5, 0x1e, 0xb4, 0x05, 0x9d,
+	0xbc, 0xd0, 0x01, 0xf7, 0x08, 0xda, 0x6a, 0xe6, 0x47, 0x24, 0x8a, 0x84, 0x7e, 0x79, 0xbb, 0x41,
+	0x4b, 0x05, 0xce, 0xa3, 0x48, 0x94, 0x06, 0xbf, 0x51, 0x1e, 0x7c, 0xf7, 0x31, 0x6c, 0x4d, 0x04,
+	0xff, 0x95, 0xc6, 0xa3, 0x19, 0x65, 0xd3, 0x19, 0x7a, 0x4d, 0xfd, 0x04, 0x77, 0x4d, 0xf0, 0x4a,
+	0xc7, 0xfc, 0x37, 0xd0, 0x1a, 0x66, 0xa6, 0x0b, 0x25, 0x20, 0x67, 0x05, 0xe8, 0x33, 0x68, 0x22,
+	0x37, 0xb9, 0x8d, 0x18, 0x37, 0x90, 0xeb, 0xcc, 0x1f, 0x64, 0xa8, 0xad, 0xc9, 0xf0, 0xbe, 0x02,
+	0x9d, 0x92, 0xa4, 0x15, 0x3a, 0xe9, 0x2c, 0x75, 0xf2, 0x7e, 0x45, 0x72, 0x9f, 0x41, 0x1b, 0xb3,
+	0x11, 0x53, 0xfd, 0x93, 0x5e, 0xf5, 0x23, 0xd7, 0xc1, 0x36, 0x39, 0x68, 0xa1, 0x31, 0xa4, 0xfb,
+	0x3d, 0x00, 0x66, 0x23, 0xae, 0x6b, 0x93, 0xfa, 0xab, 0xe6, 0xbe, 0xf1, 0xc8, 0x3b, 0x10, 0xb4,
+	0xd1, 0x5a, 0x52, 0xd1, 0x8c, 0xa8, 0x0c, 0x75, 0x4f, 0xbb, 0x81, 0xb6, 0x57, 0xbf, 0xa3, 0x0e,
+	0xff, 0xe9, 0x3b, 0xea, 0xe8, 0x83, 0xef, 0x28, 0xff, 0xb7, 0x0a, 0xd4, 0xb5, 0x50, 0x96, 0x2b,
+	0xae, 0xae, 0x56, 0x7c, 0x00, 0xad, 0x44, 0xd0, 0xd1, 0x8c, 0xc8, 0x99, 0x55, 0x85, 0x66, 0x22,
+	0xe8, 0x15, 0x91, 0x33, 0x25, 0x25, 0x89, 0xe0, 0x09, 0x97, 0xb4, 0x98, 0x82, 0xdc, 0x57, 0x7c,
+	0x25, 0x9b, 0xc6, 0x76, 0x06, 0xb4, 0xad, 0x0e, 0x34, 0x49, 0xc7, 0xea, 0x61, 0x6d, 0x9a, 0x73,
+	0x33, 0x9e, 0x8a, 0xdb, 0x03, 0x6b, 0xeb, 0x03, 0xb3, 0x9e, 0x7a, 0x73, 0xd5, 0x71, 0x48, 0xaf,
+	0xa3, 0xa9, 0x1b, 0x47, 0x11, 0xc2, 0x6c, 0x14, 0xea, 0xc1, 0xe8, 0xea, 0xc9, 0x6c, 0x62, 0x76,
+	0xa1, 0x27, 0xe3, 0x00, 0x5a, 0x2c, 0x1e, 0xa1, 0x48, 0xe3, 0x5b, 0xaf, 0xd7, 0x77, 0x06, 0xad,
+	0xa0, 0xc9, 0xe2, 0xa1, 0x72, 0xd5, 0xc8, 0xc6, 0x34, 0x43, 0x53, 0xc7, 0xb6, 0xd1, 0x3d, 0x15,
+	0x50, 0x85, 0xf8, 0x02, 0x0e, 0x2f, 0x29, 0x9e, 0x87, 0x1a, 0x54, 0x8d, 0x12, 0x95, 0x92, 0xca,
+	0x0d, 0xbd, 0x6d, 0xc4, 0xc0, 0xe6, 0x93, 0x64, 0x5d, 0xff, 0x3b, 0x38, 0x5a, 0x9b, 0xd3, 0x2a,
+	0xdc, 0x31, 0xb4, 0x49, 0x1e, 0xd4, 0x5f, 0x12, 0xed, 0x60, 0x19, 0x78, 0xfe, 0xf4, 0xaa, 0xfa,
+	0xfa, 0x6c, 0xca, 0x70, 0x96, 0x8e, 0x4f, 0x42, 0xbe, 0x38, 0xcd, 0xd2, 0x84, 0x0a, 0x4d, 0xca,
+	0x98, 0x69, 0xcc, 0x78, 0x7c, 0x9a, 0xf3, 0x93, 0xd1, 0xed, 0xe9, 0x94, 0x9f, 0x26, 0xe3, 0x71,
+	0x43, 0xff, 0xff, 0xf8, 0xfa, 0xef, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb8, 0x22, 0x7b, 0x4f, 0x91,
+	0x0c, 0x00, 0x00,
 }
diff --git a/contractsdk/pb/contract.proto b/contractsdk/pb/contract.proto
index b996305..8a87361 100644
--- a/contractsdk/pb/contract.proto
+++ b/contractsdk/pb/contract.proto
@@ -28,6 +28,7 @@ message CallArgs {
   repeated ArgPair args = 2;
   string initiator = 3;
   repeated string auth_require = 4;
+  string transfer_amount = 5;
 }
 
 message SyscallHeader {
diff --git a/txn/tx.go b/txn/tx.go
new file mode 100644
index 0000000..8cf23aa
--- /dev/null
+++ b/txn/tx.go
@@ -0,0 +1,37 @@
+// package txn deals with tx data
+package txn
+
+import (
+	"errors"
+	"math/big"
+
+	"github.com/xuperchain/xuperunion/pb"
+)
+
+// Transaction is the internal represents of transaction
+type Transaction struct {
+	*pb.Transaction
+}
+
+func ParseContractTransferRequest(requests []*pb.InvokeRequest) (string, *big.Int, error) {
+	// found is the flag of whether the contract already carries the amount parameter
+	var found bool
+	amount := new(big.Int)
+	var contractName string
+	for _, req := range requests {
+		amountstr := req.GetAmount()
+		if amountstr == "" {
+			continue
+		}
+		if found {
+			return "", nil, errors.New("duplicated contract transfer amount")
+		}
+		_, ok := amount.SetString(amountstr, 10)
+		if !ok {
+			return "", nil, errors.New("bad amount in request")
+		}
+		found = true
+		contractName = req.GetContractName()
+	}
+	return contractName, amount, nil
+}
diff --git a/utxo/tx_verification.go b/utxo/tx_verification.go
index 4bb583c..46bca7a 100644
--- a/utxo/tx_verification.go
+++ b/utxo/tx_verification.go
@@ -21,6 +21,7 @@ import (
 	pm "github.com/xuperchain/xuperunion/permission"
 	"github.com/xuperchain/xuperunion/permission/acl"
 	aclu "github.com/xuperchain/xuperunion/permission/acl/utils"
+	"github.com/xuperchain/xuperunion/txn"
 	"github.com/xuperchain/xuperunion/utxo/txhash"
 	"github.com/xuperchain/xuperunion/xmodel"
 	xmodel_pb "github.com/xuperchain/xuperunion/xmodel/pb"
@@ -103,7 +104,7 @@ func (uv *UtxoVM) ImmediateVerifyTx(tx *pb.Transaction, isRootTx bool) (bool, er
 		// verify amount of transfer within contract
 		ok, err = uv.verifyContractTxAmount(tx)
 		if !ok {
-			uv.xlog.Warn("ImmediateVerifyTx: verifyContractPermission failed", "error", err)
+			uv.xlog.Warn("ImmediateVerifyTx: verifyContractTxAmount failed", "error", err)
 			return ok, ErrContractTxAmout
 		}
 
@@ -258,7 +259,7 @@ func (uv *UtxoVM) verifyXuperSign(tx *pb.Transaction, digestHash []byte) (bool,
 //	3). Contract logic transferring from contract
 func (uv *UtxoVM) verifyUTXOPermission(tx *pb.Transaction, verifiedID map[string]bool) (bool, error) {
 	// verify tx input
-	conUtxoInputs, _, err := xmodel.ParseContractUtxo(tx)
+	conUtxoInputs, err := xmodel.ParseContractUtxoInputs(tx)
 	if err != nil {
 		uv.xlog.Warn("verifyUTXOPermission error, parseContractUtxo ")
 		return false, ErrParseContractUtxos
@@ -442,35 +443,15 @@ func getGasLimitFromTx(tx *pb.Transaction) (int64, error) {
 
 // verifyContractTxAmount verify
 func (uv *UtxoVM) verifyContractTxAmount(tx *pb.Transaction) (bool, error) {
-	req := tx.GetContractRequests()
-	// af is the flag of whether the contract already carries the amount parameter
-	af := false
-	amountCon := new(big.Int).SetInt64(0)
 	amountOut := new(big.Int).SetInt64(0)
-	var contractName string
-	for i := 0; i < len(req); i++ {
-		tmpReq := req[i]
-		if af {
-			return false, ErrInvokeReqParams
-		}
-		if tmpReq.GetAmount() != "" {
-			amountCon, ok := amountCon.SetString(tmpReq.GetAmount(), 10)
-			if !ok {
-				return false, ErrInvokeReqParams
-			}
-			if amountCon.Cmp(new(big.Int).SetInt64(0)) == 1 {
-				af = true
-				contractName = tmpReq.GetContractName()
-			}
-		}
+	req := tx.GetContractRequests()
+	contractName, amountCon, err := txn.ParseContractTransferRequest(req)
+	if err != nil {
+		return false, err
 	}
-
 	for _, txOutput := range tx.GetTxOutputs() {
 		if string(txOutput.GetToAddr()) == contractName {
-			tmpAmount, ok := new(big.Int).SetString(string(txOutput.GetAmount()), 10)
-			if !ok {
-				return false, ErrInvokeReqParams
-			}
+			tmpAmount := new(big.Int).SetBytes(txOutput.GetAmount())
 			amountOut.Add(tmpAmount, amountOut)
 		}
 	}
@@ -507,6 +488,11 @@ func (uv *UtxoVM) verifyTxRWSets(tx *pb.Transaction) (bool, error) {
 		}
 		return true, nil
 	}
+	// transfer in contract
+	transContractName, transAmount, err := txn.ParseContractTransferRequest(req)
+	if err != nil {
+		return false, err
+	}
 
 	env, err := uv.model3.PrepareEnv(tx)
 	if err != nil {
@@ -520,6 +506,7 @@ func (uv *UtxoVM) verifyTxRWSets(tx *pb.Transaction) (bool, error) {
 		Core: contractChainCore{
 			Manager: uv.aclMgr,
 		},
+		BCName: uv.bcname,
 	}
 	gasLimit, err := getGasLimitFromTx(tx)
 	if err != nil {
@@ -545,6 +532,12 @@ func (uv *UtxoVM) verifyTxRWSets(tx *pb.Transaction) (bool, error) {
 		}
 		contextConfig.ResourceLimits = limits
 		contextConfig.ContractName = tmpReq.GetContractName()
+		if transContractName == tmpReq.GetContractName() {
+			contextConfig.TransferAmount = transAmount.String()
+		} else {
+			contextConfig.TransferAmount = ""
+		}
+
 		ctx, err := vm.NewContext(contextConfig)
 		if err != nil {
 			// FIXME zq @icexin: need to return contract not found
@@ -564,6 +557,11 @@ func (uv *UtxoVM) verifyTxRWSets(tx *pb.Transaction) (bool, error) {
 
 		ctx.Release()
 	}
+	utxoInputs, utxoOutputs := env.GetModelCache().GetUtxoRWSets()
+	err = env.GetModelCache().PutUtxos(utxoInputs, utxoOutputs)
+	if err != nil {
+		return false, err
+	}
 
 	_, writeSet, err := env.GetModelCache().GetRWSets()
 	if err != nil {
@@ -572,7 +570,7 @@ func (uv *UtxoVM) verifyTxRWSets(tx *pb.Transaction) (bool, error) {
 	uv.xlog.Trace("verifyTxRWSets", "env.output", env.GetOutputs(), "writeSet", writeSet)
 	ok := xmodel.Equal(env.GetOutputs(), writeSet)
 	if !ok {
-		return false, fmt.Errorf("Verify error")
+		return false, fmt.Errorf("write set not equal")
 	}
 	return true, nil
 }
diff --git a/utxo/utxo.go b/utxo/utxo.go
index 83772cf..47e1925 100755
--- a/utxo/utxo.go
+++ b/utxo/utxo.go
@@ -35,6 +35,7 @@ import (
 	"github.com/xuperchain/xuperunion/permission/acl"
 	acli "github.com/xuperchain/xuperunion/permission/acl/impl"
 	"github.com/xuperchain/xuperunion/permission/acl/utils"
+	"github.com/xuperchain/xuperunion/txn"
 	"github.com/xuperchain/xuperunion/utxo/txhash"
 	"github.com/xuperchain/xuperunion/vat"
 	"github.com/xuperchain/xuperunion/xmodel"
@@ -147,6 +148,7 @@ type UtxoVM struct {
 	maxConfirmedDelay    uint32    // 交易处于unconfirm状态的最长时间，超过后会被回滚
 	unconfirmTxAmount    int64     // 未确认的Tx数目，用于监控
 	avgDelay             int64     // 平均上链延时
+	bcname               string
 }
 
 // InboundTx is tx wrapper
@@ -364,7 +366,7 @@ func MakeUtxoVM(bcname string, ledger *ledger_pkg.Ledger, storePath string, priv
 	}
 
 	// create model3
-	model3, mErr := xmodel.NewXuperModel(bcname, ledger, baseDB, xlog)
+	model3, mErr := xmodel.NewXuperModel(ledger, baseDB, xlog)
 	if mErr != nil {
 		xlog.Warn("failed to init xuper model", "err", mErr)
 		return nil, mErr
@@ -426,6 +428,7 @@ func MakeUtxoVM(bcname string, ledger *ledger_pkg.Ledger, storePath string, priv
 		vmMgr3:               vmManager,
 		aclMgr:               aclManager,
 		maxConfirmedDelay:    DefaultMaxConfirmedDelay,
+		bcname:               bcname,
 	}
 	if iBeta {
 		utxoVM.defaultTxVersion = BetaTxVersion
@@ -758,8 +761,14 @@ func (uv *UtxoVM) PreExec(req *pb.InvokeRPCRequest, hd *global.XContext) (*pb.In
 	// contract request with reservedRequests
 	req.Requests = append(reservedRequests, req.Requests...)
 	uv.xlog.Trace("PreExec requests after merge", "requests", req.Requests)
+
+	// transfer in contract
+	transContractName, transAmount, err := txn.ParseContractTransferRequest(req.Requests)
+	if err != nil {
+		return nil, err
+	}
 	// init modelCache
-	modelCache, err := xmodel.NewXModelCache(uv.GetXModel(), true)
+	modelCache, err := xmodel.NewXModelCache(uv.GetXModel(), uv)
 	if err != nil {
 		return nil, err
 	}
@@ -775,6 +784,7 @@ func (uv *UtxoVM) PreExec(req *pb.InvokeRPCRequest, hd *global.XContext) (*pb.In
 		Core: contractChainCore{
 			Manager: uv.aclMgr,
 		},
+		BCName: uv.bcname,
 	}
 	gasUesdTotal := int64(0)
 	response := [][]byte{}
@@ -810,6 +820,11 @@ func (uv *UtxoVM) PreExec(req *pb.InvokeRPCRequest, hd *global.XContext) (*pb.In
 		}
 
 		contextConfig.ContractName = tmpReq.GetContractName()
+		if transContractName == tmpReq.GetContractName() {
+			contextConfig.TransferAmount = transAmount.String()
+		} else {
+			contextConfig.TransferAmount = ""
+		}
 		ctx, err := vm.NewContext(contextConfig)
 		if err != nil {
 			// FIXME zq @icexin need to return contract not found error
@@ -840,18 +855,25 @@ func (uv *UtxoVM) PreExec(req *pb.InvokeRPCRequest, hd *global.XContext) (*pb.In
 		requests = append(requests, &request)
 		ctx.Release()
 	}
+	utxoInputs, utxoOutputs := modelCache.GetUtxoRWSets()
+	err = modelCache.PutUtxos(utxoInputs, utxoOutputs)
+	if err != nil {
+		return nil, err
+	}
 
 	inputs, outputs, err := modelCache.GetRWSets()
 	if err != nil {
 		return nil, err
 	}
 	rsps := &pb.InvokeResponse{
-		Inputs:    xmodel.GetTxInputs(inputs),
-		Outputs:   xmodel.GetTxOutputs(outputs),
-		Response:  response,
-		Requests:  requests,
-		GasUsed:   gasUesdTotal,
-		Responses: responses,
+		Inputs:      xmodel.GetTxInputs(inputs),
+		Outputs:     xmodel.GetTxOutputs(outputs),
+		Response:    response,
+		Requests:    requests,
+		GasUsed:     gasUesdTotal,
+		Responses:   responses,
+		UtxoInputs:  utxoInputs,
+		UtxoOutputs: utxoOutputs,
 	}
 	return rsps, nil
 }
@@ -1853,7 +1875,7 @@ func (uv *UtxoVM) queryTxFromForbiddenWithConfirmed(txid []byte) (bool, bool, er
 			"txid": []byte(fmt.Sprintf("%x", txid)),
 		},
 	}
-	modelCache, err := xmodel.NewXModelCache(uv.GetXModel(), true)
+	modelCache, err := xmodel.NewXModelCache(uv.GetXModel(), uv)
 	if err != nil {
 		return false, false, err
 	}
@@ -2219,7 +2241,7 @@ func (uv *UtxoVM) queryContractBannedStatus(contractName string) (bool, error) {
 		},
 	}
 
-	modelCache, err := xmodel.NewXModelCache(uv.GetXModel(), true)
+	modelCache, err := xmodel.NewXModelCache(uv.GetXModel(), uv)
 	if err != nil {
 		uv.xlog.Warn("queryContractBannedStatus new model cache error", "error", err)
 		return false, err
diff --git a/xmodel/dbutils.go b/xmodel/dbutils.go
index 77def68..8253957 100644
--- a/xmodel/dbutils.go
+++ b/xmodel/dbutils.go
@@ -6,7 +6,7 @@ import (
 	"sort"
 
 	"github.com/golang/protobuf/proto"
-	"github.com/xuperchain/log15"
+	log "github.com/xuperchain/log15"
 	"github.com/xuperchain/xuperunion/kv/kvdb"
 	"github.com/xuperchain/xuperunion/pb"
 	xmodel_pb "github.com/xuperchain/xuperunion/xmodel/pb"
@@ -86,15 +86,13 @@ func openDB(dbPath string, logger log.Logger) (kvdb.Database, error) {
 }
 
 // 快速对写集合排序
-type pdSlice []xmodel_pb.PureData
+type pdSlice []*xmodel_pb.PureData
 
 // newPdSlice new a slice instance for PureData
 func newPdSlice(vpd []*xmodel_pb.PureData) pdSlice {
-	pds := []xmodel_pb.PureData{}
-	for _, v := range vpd {
-		pds = append(pds, *v)
-	}
-	return pds
+	s := make([]*xmodel_pb.PureData, len(vpd))
+	copy(s, vpd)
+	return s
 }
 
 // Len length of slice of PureData
@@ -109,24 +107,24 @@ func (pds pdSlice) Swap(i, j int) {
 
 // Less compare two pureData elements with pureData's key in a slice
 func (pds pdSlice) Less(i, j int) bool {
-	rawKeyI := string(makeRawKey(pds[i].GetBucket(), pds[i].GetKey()))
-	rawKeyJ := string(makeRawKey(pds[j].GetBucket(), pds[j].GetKey()))
-	if rawKeyI == rawKeyJ {
+	rawKeyI := makeRawKey(pds[i].GetBucket(), pds[i].GetKey())
+	rawKeyJ := makeRawKey(pds[j].GetBucket(), pds[j].GetKey())
+	ret := bytes.Compare(rawKeyI, rawKeyJ)
+	if ret == 0 {
 		// 注: 正常应该无法走到这个逻辑，因为写集合中的key一定是唯一的
-		return string(pds[i].GetValue()) < string(pds[j].GetValue())
+		return bytes.Compare(pds[i].GetValue(), pds[j].GetValue()) < 0
 	}
-	return rawKeyI < rawKeyJ
+	return ret < 0
 }
 
-func equal(pd, vpd xmodel_pb.PureData) bool {
-	rawKeyI := string(makeRawKey(pd.GetBucket(), pd.GetKey()))
-	rawKeyJ := string(makeRawKey(vpd.GetBucket(), vpd.GetKey()))
-	if rawKeyI != rawKeyJ {
+func equal(pd, vpd *xmodel_pb.PureData) bool {
+	rawKeyI := makeRawKey(pd.GetBucket(), pd.GetKey())
+	rawKeyJ := makeRawKey(vpd.GetBucket(), vpd.GetKey())
+	ret := bytes.Compare(rawKeyI, rawKeyJ)
+	if ret != 0 {
 		return false
 	}
-	valI := string(pd.GetValue())
-	valJ := string(vpd.GetValue())
-	return valI == valJ
+	return bytes.Equal(pd.GetValue(), vpd.GetValue())
 }
 
 // Equal check if two PureData object equal
diff --git a/xmodel/env.go b/xmodel/env.go
index e03e613..86173a5 100644
--- a/xmodel/env.go
+++ b/xmodel/env.go
@@ -35,11 +35,11 @@ func (s *XModel) PrepareEnv(tx *pb.Transaction) (*Env, error) {
 	for _, txOut := range tx.TxOutputsExt {
 		outputs = append(outputs, &xmodel_pb.PureData{Bucket: txOut.Bucket, Key: txOut.Key, Value: txOut.Value})
 	}
-	modelCache, err := NewXModelCache(s, false)
+	utxoInputs, err := ParseContractUtxoInputs(tx)
 	if err != nil {
 		return nil, err
 	}
-	env.modelCache = modelCache
+	env.modelCache = NewXModelCacheWithInputs(inputs, utxoInputs)
 
 	for _, verData := range inputs {
 		env.modelCache.fill(verData)
@@ -65,8 +65,3 @@ func (e *Env) GetOutputs() []*xmodel_pb.PureData {
 	}
 	return nil
 }
-
-// ParseContractUtxo parse contract utxos from tx write sets
-func ParseContractUtxo(tx *pb.Transaction) ([]*pb.TxInput, []*pb.TxOutput, error) {
-	return nil, nil, nil
-}
diff --git a/xmodel/message.go b/xmodel/message.go
new file mode 100644
index 0000000..468a6d2
--- /dev/null
+++ b/xmodel/message.go
@@ -0,0 +1,87 @@
+package xmodel
+
+import (
+	"errors"
+	"fmt"
+	"reflect"
+
+	"github.com/golang/protobuf/proto"
+)
+
+var (
+	protoIface = reflect.TypeOf((*proto.Message)(nil)).Elem()
+)
+
+// marshalMessages marshal protobuf message slice
+func marshalMessages(msgs interface{}) ([]byte, error) {
+	if msgs == nil {
+		return nil, nil
+	}
+	value := reflect.ValueOf(msgs)
+	tp := value.Type()
+	if tp.Kind() != reflect.Slice {
+		return nil, errors.New("bad slice type")
+	}
+	if !tp.Elem().Implements(protoIface) {
+		return nil, errors.New("elem of slice must be protobuf message")
+	}
+	if value.Len() == 0 {
+		return nil, nil
+	}
+
+	var buf proto.Buffer
+	buf.EncodeVarint(uint64(value.Len()))
+	for i := 0; i < value.Len(); i++ {
+		msg := value.Index(i).Interface().(proto.Message)
+		err := buf.EncodeMessage(msg)
+		if err != nil {
+			return nil, err
+		}
+	}
+	return buf.Bytes(), nil
+}
+
+// unmsarshalMessages unmarshal protobuf messages to slice, x must be a pointer to message slice
+func unmsarshalMessages(p []byte, x interface{}) error {
+	if p == nil {
+		return nil
+	}
+	tp := reflect.TypeOf(x)
+	// x must be a pointer to message slice
+	if tp.Kind() != reflect.Ptr {
+		return errors.New("must be slice ptr")
+	}
+	tp = tp.Elem()
+	if tp.Kind() != reflect.Slice {
+		return errors.New("must be slice ptr")
+	}
+	// element of slice must be proto.Message
+	if !tp.Elem().Implements(protoIface) {
+		return errors.New("elem of slice must be protobuf message")
+	}
+	// element of slice must be ptr type
+	if tp.Elem().Kind() != reflect.Ptr {
+		return errors.New("elem of slice must be ptr type")
+	}
+	// if tp is []*pb.TxInput then elemtp is pb.TxInput
+	elemtp := tp.Elem().Elem()
+	value := reflect.ValueOf(x).Elem()
+
+	buf := proto.NewBuffer(p)
+	total, err := buf.DecodeVarint()
+	if err != nil {
+		return fmt.Errorf("error while read message length:%s", err)
+	}
+
+	value.Set(reflect.MakeSlice(tp, int(total), int(total)))
+	for i := 0; i < int(total); i++ {
+		v := reflect.New(elemtp)
+		m := v.Interface().(proto.Message)
+		err = buf.DecodeMessage(m)
+		if err != nil {
+			return fmt.Errorf("error while unmsarshal message:%s", err)
+		}
+		value.Index(i).Set(v)
+	}
+	return nil
+}
diff --git a/xmodel/utxo_cache.go b/xmodel/utxo_cache.go
new file mode 100644
index 0000000..44ef9ac
--- /dev/null
+++ b/xmodel/utxo_cache.go
@@ -0,0 +1,94 @@
+package xmodel
+
+import (
+	"bytes"
+	"errors"
+	"math/big"
+
+	"github.com/xuperchain/xuperunion/pb"
+)
+
+// UtxoCache makes utxo rwset
+type UtxoCache struct {
+	utxovm      UtxoVM
+	inputCache  []*pb.TxInput
+	outputCache []*pb.TxOutput
+	intputIdx   int
+	isPenetrate bool
+}
+
+func NewUtxoCache(utxovm UtxoVM) *UtxoCache {
+	return &UtxoCache{
+		utxovm:      utxovm,
+		isPenetrate: true,
+	}
+}
+
+func NewUtxoCacheWithInputs(inputs []*pb.TxInput) *UtxoCache {
+	return &UtxoCache{
+		inputCache:  inputs,
+		isPenetrate: false,
+	}
+}
+
+func (u *UtxoCache) selectUtxos(from string, amount *big.Int) (*big.Int, error) {
+	if u.isPenetrate {
+		inputs, _, total, err := u.utxovm.SelectUtxos(from, "", amount, false, false)
+		if err != nil {
+			return nil, err
+		}
+		u.inputCache = append(u.inputCache, inputs...)
+		return total, nil
+	}
+
+	fromBytes := []byte(from)
+	inputCache := u.inputCache[u.intputIdx:]
+	sum := new(big.Int)
+	n := 0
+	for _, input := range inputCache {
+		n++
+		// Since contract calls bridge serially, a mismatched from address is an error
+		if !bytes.Equal(input.GetFromAddr(), fromBytes) {
+			return nil, errors.New("from address mismatch in utxo cache")
+		}
+		sum.Add(sum, new(big.Int).SetBytes(input.GetAmount()))
+		if sum.Cmp(amount) >= 0 {
+			break
+		}
+	}
+	if sum.Cmp(amount) < 0 {
+		return nil, errors.New("utxo not enough in utxo cache")
+	}
+	u.intputIdx += n
+	return sum, nil
+}
+
+func (u *UtxoCache) Transfer(from, to string, amount *big.Int) error {
+	if amount.Cmp(new(big.Int)) == 0 {
+		return nil
+	}
+	total, err := u.selectUtxos(from, amount)
+	if err != nil {
+		return err
+	}
+	u.outputCache = append(u.outputCache, &pb.TxOutput{
+		Amount: amount.Bytes(),
+		ToAddr: []byte(to),
+	})
+	// make change
+	if total.Cmp(amount) > 0 {
+		u.outputCache = append(u.outputCache, &pb.TxOutput{
+			Amount: new(big.Int).Sub(total, amount).Bytes(),
+			ToAddr: []byte(from),
+		})
+	}
+
+	return nil
+}
+
+func (u *UtxoCache) GetRWSets() ([]*pb.TxInput, []*pb.TxOutput) {
+	if u.isPenetrate {
+		return u.inputCache, u.outputCache
+	}
+	return u.inputCache[:u.intputIdx], u.outputCache
+}
diff --git a/xmodel/versioned_data.go b/xmodel/versioned_data.go
index 6529258..c9a8eab 100644
--- a/xmodel/versioned_data.go
+++ b/xmodel/versioned_data.go
@@ -49,7 +49,7 @@ func GetVersionOfTxInput(txIn *pb.TxInputExt) string {
 
 // GetTxOutputs get transaction outputs
 func GetTxOutputs(pds []*xmodel_pb.PureData) []*pb.TxOutputExt {
-	outputs := []*pb.TxOutputExt{}
+	outputs := make([]*pb.TxOutputExt, 0, len(pds))
 	for _, pd := range pds {
 		outputs = append(outputs, &pb.TxOutputExt{
 			Bucket: pd.Bucket,
@@ -62,7 +62,7 @@ func GetTxOutputs(pds []*xmodel_pb.PureData) []*pb.TxOutputExt {
 
 // GetTxInputs get transaction inputs
 func GetTxInputs(vds []*xmodel_pb.VersionedData) []*pb.TxInputExt {
-	inputs := []*pb.TxInputExt{}
+	inputs := make([]*pb.TxInputExt, 0, len(vds))
 	for _, vd := range vds {
 		inputs = append(inputs, &pb.TxInputExt{
 			Bucket:    vd.GetPureData().GetBucket(),
diff --git a/xmodel/xmodel.go b/xmodel/xmodel.go
index 72de3a1..0fced4e 100644
--- a/xmodel/xmodel.go
+++ b/xmodel/xmodel.go
@@ -14,11 +14,14 @@ import (
 
 const (
 	bucketExtUTXOCacheSize = 1024
+
+	// TransientBucket is the name of bucket that only appears in tx output set
+	// but does't persists in xmodel
+	TransientBucket = "$transient"
 )
 
 // XModel xmodel data structure
 type XModel struct {
-	bcname          string
 	ledger          *ledger.Ledger
 	stateDB         kvdb.Database
 	unconfirmTable  kvdb.Database
@@ -32,9 +35,8 @@ type XModel struct {
 }
 
 // NewXuperModel new an instance of XModel
-func NewXuperModel(bcname string, ledger *ledger.Ledger, stateDB kvdb.Database, logger log.Logger) (*XModel, error) {
+func NewXuperModel(ledger *ledger.Ledger, stateDB kvdb.Database, logger log.Logger) (*XModel, error) {
 	return &XModel{
-		bcname:          bcname,
 		ledger:          ledger,
 		stateDB:         stateDB,
 		unconfirmTable:  kvdb.NewTable(stateDB, pb.UnconfirmedTablePrefix),
@@ -47,6 +49,9 @@ func NewXuperModel(bcname string, ledger *ledger.Ledger, stateDB kvdb.Database,
 
 func (s *XModel) updateExtUtxo(tx *pb.Transaction, batch kvdb.Batch) error {
 	for offset, txOut := range tx.TxOutputsExt {
+		if txOut.Bucket == TransientBucket {
+			continue
+		}
 		bucketAndKey := makeRawKey(txOut.Bucket, txOut.Key)
 		valueVersion := MakeVersion(tx.Txid, int32(offset))
 		if isDelFlag(txOut.Value) {
@@ -103,6 +108,9 @@ func (s *XModel) UndoTx(tx *pb.Transaction, batch kvdb.Batch) error {
 		inputVersionMap[rawKey] = version
 	}
 	for _, txOut := range tx.TxOutputsExt {
+		if txOut.Bucket == TransientBucket {
+			continue
+		}
 		bucketAndKey := makeRawKey(txOut.Bucket, txOut.Key)
 		previousVersion := inputVersionMap[string(bucketAndKey)]
 		if previousVersion == "" {
diff --git a/xmodel/xmodel_cache.go b/xmodel/xmodel_cache.go
index 734766d..1a89a11 100644
--- a/xmodel/xmodel_cache.go
+++ b/xmodel/xmodel_cache.go
@@ -1,7 +1,9 @@
 package xmodel
 
 import (
+	"bytes"
 	"errors"
+	"math/big"
 
 	"github.com/syndtr/goleveldb/leveldb/comparer"
 	"github.com/syndtr/goleveldb/leveldb/util"
@@ -24,6 +26,16 @@ var (
 	ErrNotFound = errors.New("Key not found")
 )
 
+var (
+	contractUtxoInputKey  = []byte("ContractUtxo.Inputs")
+	contractUtxoOutputKey = []byte("ContractUtxo.Outputs")
+)
+
+// UtxoVM manages utxos
+type UtxoVM interface {
+	SelectUtxos(fromAddr string, fromPubKey string, totalNeed *big.Int, needLock, excludeUnconfirmed bool) ([]*pb.TxInput, [][]byte, *big.Int, error)
+}
+
 // XMCache data structure for XModel Cache
 type XMCache struct {
 	// Key: bucket_key; Value: VersionedData
@@ -33,18 +45,37 @@ type XMCache struct {
 	// 是否穿透到model层
 	isPenetrate bool
 	model       *XModel
+	utxoCache   *UtxoCache
 }
 
 // NewXModelCache new an instance of XModel Cache
-func NewXModelCache(model *XModel, isPenetrate bool) (*XMCache, error) {
+func NewXModelCache(model *XModel, utxovm UtxoVM) (*XMCache, error) {
 	return &XMCache{
-		isPenetrate:  isPenetrate,
+		isPenetrate:  true,
 		model:        model,
 		inputsCache:  memdb.New(comparer.DefaultComparer, DefaultMemDBSize),
 		outputsCache: memdb.New(comparer.DefaultComparer, DefaultMemDBSize),
+		utxoCache:    NewUtxoCache(utxovm),
 	}, nil
 }
 
+func NewXModelCacheWithInputs(vdatas []*xmodel_pb.VersionedData, utxoInputs []*pb.TxInput) *XMCache {
+	xc := &XMCache{
+		isPenetrate:  false,
+		inputsCache:  memdb.New(comparer.DefaultComparer, DefaultMemDBSize),
+		outputsCache: memdb.New(comparer.DefaultComparer, DefaultMemDBSize),
+	}
+	for _, vd := range vdatas {
+		bucket := vd.GetPureData().GetBucket()
+		key := vd.GetPureData().GetKey()
+		rawKey := makeRawKey(bucket, key)
+		valBuf, _ := proto.Marshal(vd)
+		xc.inputsCache.Put(rawKey, valBuf)
+	}
+	xc.utxoCache = NewUtxoCacheWithInputs(utxoInputs)
+	return xc
+}
+
 // Get 读取一个key的值，返回的value就是有版本的data
 func (xc *XMCache) Get(bucket string, key []byte) (*xmodel_pb.VersionedData, error) {
 	// Level1: get from outputsCache
@@ -149,8 +180,10 @@ func (xc *XMCache) Put(bucket string, key []byte, value []byte) error {
 	if err != nil {
 		return err
 	}
-	// put 前先强制get一下
-	xc.Get(bucket, key)
+	if bucket != TransientBucket {
+		// put 前先强制get一下
+		xc.Get(bucket, key)
+	}
 	return xc.outputsCache.Put(buKey, valBuf)
 }
 
@@ -243,7 +276,99 @@ func (xc *XMCache) fill(vd *xmodel_pb.VersionedData) error {
 	return xc.inputsCache.Put(rawKey, valBuf)
 }
 
-// GetBcname 返回bcname
-func (xc *XMCache) GetBcname() string {
-	return xc.model.bcname
+// Transfer transfer tokens using utxo
+func (xc *XMCache) Transfer(from, to string, amount *big.Int) error {
+	return xc.utxoCache.Transfer(from, to, amount)
+}
+
+// GetUtxoRWSets returns the inputs and outputs of utxo
+func (xc *XMCache) GetUtxoRWSets() ([]*pb.TxInput, []*pb.TxOutput) {
+	return xc.utxoCache.GetRWSets()
+}
+
+func (xc *XMCache) PutUtxos(inputs []*pb.TxInput, outputs []*pb.TxOutput) error {
+	var in, out []byte
+	var err error
+	if len(inputs) != 0 {
+		in, err = marshalMessages(inputs)
+		if err != nil {
+			return err
+		}
+	}
+	if len(outputs) != 0 {
+		out, err = marshalMessages(outputs)
+		if err != nil {
+			return err
+		}
+	}
+	if in != nil {
+		err = xc.Put(TransientBucket, contractUtxoInputKey, in)
+		if err != nil {
+			return err
+		}
+	}
+	if out != nil {
+		err = xc.Put(TransientBucket, contractUtxoOutputKey, out)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// ParseContractUtxo parse contract utxo inputs from tx write sets
+func ParseContractUtxoInputs(tx *pb.Transaction) ([]*pb.TxInput, error) {
+	var (
+		utxoInputs []*pb.TxInput
+		extInput   []byte
+	)
+	for _, out := range tx.GetTxOutputsExt() {
+		if out.GetBucket() != TransientBucket {
+			continue
+		}
+		if bytes.Equal(out.GetKey(), contractUtxoInputKey) {
+			extInput = out.GetValue()
+		}
+	}
+	if extInput != nil {
+		err := unmsarshalMessages(extInput, &utxoInputs)
+		if err != nil {
+			return nil, err
+		}
+	}
+	return utxoInputs, nil
+}
+
+// ParseContractUtxo parse contract utxos from tx write sets
+func ParseContractUtxo(tx *pb.Transaction) ([]*pb.TxInput, []*pb.TxOutput, error) {
+	var (
+		utxoInputs  []*pb.TxInput
+		utxoOutputs []*pb.TxOutput
+		extInput    []byte
+		extOutput   []byte
+	)
+	for _, out := range tx.GetTxOutputsExt() {
+		if out.GetBucket() != TransientBucket {
+			continue
+		}
+		if bytes.Equal(out.GetKey(), contractUtxoInputKey) {
+			extInput = out.GetValue()
+		}
+		if bytes.Equal(out.GetKey(), contractUtxoOutputKey) {
+			extOutput = out.GetValue()
+		}
+	}
+	if extInput != nil {
+		err := unmsarshalMessages(extInput, &utxoInputs)
+		if err != nil {
+			return nil, nil, err
+		}
+	}
+	if extOutput != nil {
+		err := unmsarshalMessages(extOutput, &utxoOutputs)
+		if err != nil {
+			return nil, nil, err
+		}
+	}
+	return utxoInputs, utxoOutputs, nil
 }
diff --git a/xmodel/xmodel_verify.go b/xmodel/xmodel_verify.go
index 7562467..52abd05 100644
--- a/xmodel/xmodel_verify.go
+++ b/xmodel/xmodel_verify.go
@@ -32,6 +32,9 @@ func (s *XModel) verifyOutputs(tx *pb.Transaction) error {
 		inputKeys[rawKey] = true
 	}
 	for _, txOut := range tx.TxOutputsExt {
+		if txOut.Bucket == TransientBucket {
+			continue
+		}
 		rawKey := string(makeRawKey(txOut.Bucket, txOut.Key))
 		if !inputKeys[rawKey] {
 			return fmt.Errorf("verifyOutputs failed, not such key in txInputsExt: %s", rawKey)


Test patch:
diff --git a/contract/bridge/xbridge_test.go b/contract/bridge/xbridge_test.go
index 38c6d86..7fc80e2 100644
--- a/contract/bridge/xbridge_test.go
+++ b/contract/bridge/xbridge_test.go
@@ -54,6 +54,9 @@ func (c *codeInstance) ResourceUsed() contract.Limits {
 func (c *codeInstance) Release() {
 }
 
+func (c *codeInstance) Abort(msg string) {
+}
+
 func (c *codeInstance) testPut(args map[string][]byte) ([]byte, error) {
 	{
 		_, err := c.syscall.PutObject(context.TODO(), &pb.PutRequest{
diff --git a/contract/kernel/acl_test.go b/contract/kernel/acl_test.go
index 1ee7078..d63d785 100755
--- a/contract/kernel/acl_test.go
+++ b/contract/kernel/acl_test.go
@@ -58,7 +58,7 @@ func getXmodel(t *testing.T) *xmodel.XModel {
 	if errDB != nil {
 		t.Fatal(err)
 	}
-	xModel, errModel := xmodel.NewXuperModel("xuper", ledger, stateDB, logger)
+	xModel, errModel := xmodel.NewXuperModel(ledger, stateDB, logger)
 	if errModel != nil {
 		t.Fatal(errModel)
 	}
@@ -109,13 +109,16 @@ func TestNewAccountMethod(t *testing.T) {
 			expect:      nil,
 		},
 	}
-	modelCache, err := xmodel.NewXModelCache(xModel, true)
+	modelCache, err := xmodel.NewXModelCache(xModel, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
 	ctx := &KContext{
 		ModelCache:    modelCache,
 		ResourceLimit: contract.MaxLimits,
+		ContextConfig: &contract.ContextConfig{
+			BCName: "xuper",
+		},
 	}
 	arr := [2]string{"1", "2"}
 	for _, value := range arr {
@@ -169,7 +172,7 @@ func TestSetContractMethodAclMethod(t *testing.T) {
 		},
 	}
 
-	modelCache, err := xmodel.NewXModelCache(xModel, true)
+	modelCache, err := xmodel.NewXModelCache(xModel, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/test/util/model.go b/test/util/model.go
index c16a4a7..57930f9 100755
--- a/test/util/model.go
+++ b/test/util/model.go
@@ -70,7 +70,7 @@ func (x *XModelContext) CommitCache() error {
 
 // NewCache create model cache instance
 func (x *XModelContext) NewCache() error {
-	cache, err := xmodel.NewXModelCache(x.Model, true)
+	cache, err := xmodel.NewXModelCache(x.Model, nil)
 	if err != nil {
 		return err
 	}
@@ -125,11 +125,11 @@ func WithXModelContext(t testing.TB, callback func(x *XModelContext)) {
 		t.Fatal(err)
 	}
 	defer stateDB.Close()
-	model, err := xmodel.NewXuperModel("xuper", ledger, stateDB, logger)
+	model, err := xmodel.NewXuperModel(ledger, stateDB, logger)
 	if err != nil {
 		t.Fatal(err)
 	}
-	cache, err := xmodel.NewXModelCache(model, true)
+	cache, err := xmodel.NewXModelCache(model, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/xmodel/message_test.go b/xmodel/message_test.go
new file mode 100644
index 0000000..ae158c0
--- /dev/null
+++ b/xmodel/message_test.go
@@ -0,0 +1,61 @@
+package xmodel
+
+import (
+	"math/big"
+	"testing"
+
+	"github.com/golang/protobuf/proto"
+	"github.com/xuperchain/xuperunion/pb"
+)
+
+func TestMarshalMessages(t *testing.T) {
+	msgs := []*pb.TxInput{
+		{
+			RefTxid:   []byte("tx1"),
+			RefOffset: 1,
+			FromAddr:  []byte("fromAddr"),
+			Amount:    big.NewInt(10).Bytes(),
+		},
+		{
+			RefTxid:   []byte("tx2"),
+			RefOffset: 2,
+			FromAddr:  []byte("fromAddr"),
+			Amount:    big.NewInt(10).Bytes(),
+		},
+	}
+
+	buf, err := marshalMessages(msgs)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	var out []*pb.TxInput
+	err = unmsarshalMessages(buf, &out)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(out) != len(msgs) {
+		t.Fatalf("len not equal %d:%d", len(out), len(msgs))
+	}
+	for i := range msgs {
+		if !proto.Equal(msgs[i], out[i]) {
+			t.Fatalf("msg not equal %#v\n%#v", msgs[i], out[i])
+		}
+	}
+}
+
+func TestNilMessages(t *testing.T) {
+	var msgs []*pb.TxInput
+	var out []*pb.TxInput
+	buf, err := marshalMessages(msgs)
+	if err != nil {
+		t.Fatal(err)
+	}
+	err = unmsarshalMessages(buf, &out)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(out) != 0 {
+		t.Fatalf("unexpected length:%d", len(out))
+	}
+}
diff --git a/xmodel/xmodel_cache_test.go b/xmodel/xmodel_cache_test.go
index fb04287..c04b52c 100755
--- a/xmodel/xmodel_cache_test.go
+++ b/xmodel/xmodel_cache_test.go
@@ -114,7 +114,7 @@ func prepareXmodel(t *testing.T) *XModel {
 	if err != nil {
 		t.Fatal(err)
 	}
-	xModel, err := NewXuperModel("xuper", leg, stateDB, logger)
+	xModel, err := NewXuperModel(leg, stateDB, logger)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -128,7 +128,7 @@ func remove(path string) {
 
 func prepareXModelCache(t *testing.T) *XMCache {
 	xmodel := prepareXmodel(t)
-	mc, err := NewXModelCache(xmodel, true)
+	mc, err := NewXModelCache(xmodel, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -138,12 +138,12 @@ func prepareXModelCache(t *testing.T) *XMCache {
 func TestNewXModelCache(t *testing.T) {
 	defer remove(pathDB)
 	xmodel := prepareXmodel(t)
-	_, err := NewXModelCache(xmodel, true)
+	_, err := NewXModelCache(xmodel, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	_, err = NewXModelCache(xmodel, false)
+	_, err = NewXModelCache(xmodel, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/xmodel/xmodel_test.go b/xmodel/xmodel_test.go
index 1cfaedc..bcb10c5 100755
--- a/xmodel/xmodel_test.go
+++ b/xmodel/xmodel_test.go
@@ -38,7 +38,7 @@ func TestBaiscFunc(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	xModel, err := NewXuperModel("xuper", ledger, stateDB, logger)
+	xModel, err := NewXuperModel(ledger, stateDB, logger)
 	if err != nil {
 		t.Fatal(err)
 	}