package contract

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	chainCommon "github.com/33cn/chain33/common"
	chainTypes "github.com/33cn/chain33/types"
	"github.com/pkg/errors"
	"strconv"
	"strings"

	pabi "github.com/33cn/plugin/plugin/dapp/evm/executor/abi"

	"gitlab.33.cn/utils/go-kit/convert"

	"gitlab.33.cn/proof/micro-pkg/errcode"
	"gitlab.33.cn/proof/micro-pkg/xhttp"
)

// ContractClient 合约客户端
type ContractClient struct {
	Client       *Client
	ContractAddr string
	AbiJson      string
	Abi          *pabi.ABI
	UserAddr     string
	UserPriKey   string
}

// NewContractClient new a client
func NewContractClient(c *Client, addr, abi, user, prikey string) (*ContractClient, error) {
	cc, err := NewContractQueryClient(c, addr, abi)
	if err != nil {
		return nil, err
	}
	return cc.SetAuth(user, prikey), nil
}

// NewContractQueryClient new a query client
func NewContractQueryClient(c *Client, addr, abi string) (*ContractClient, error) {
	contractABI, err := pabi.JSON(strings.NewReader(abi))
	if err != nil {
		return nil, err
	}
	return &ContractClient{
		Client:       c,
		ContractAddr: addr,
		Abi:          &contractABI,
		AbiJson:      abi,
	}, nil
}

// SetAuth 设置使用者信息
func (c *ContractClient) SetAuth(addr, prikey string) *ContractClient {
	c.UserAddr = addr
	c.UserPriKey = prikey
	return c
}

// ContractCall 合于调用
func (c *ContractClient) ContractCall(param string) error {
	if c.UserAddr == "" || c.UserPriKey == "" {
		return errcode.ErrOptAuth
	}
	// 创建操作合约交易
	resp, err := c.Client.CreateCallTx(&CreateCallTxReq{
		Abi:          c.AbiJson,
		Parameter:    param,
		Expire:       "300s",
		ContractAddr: c.ContractAddr,
		ParaName:     c.Client.paraName,
	})
	if err != nil {
		return err
	}

	// 2)
	// 估算部署交易或者调用交易需要的 gas
	resp1, err := c.Client.EstimateGas(&EstimateGasReq{
		Tx:   resp.Result,
		From: c.UserAddr,
	})
	if err != nil {
		return err
	}

	// 3)
	// 签名
	resp2, err := c.Client.SignRawTx(&SignRawTxReq{
		Privkey: c.UserPriKey,
		TxHex:   resp.Result,
		Expire:  "300s",
		// Index:     0,
		// Token:     "",
		Fee: convert.ToInt64(resp1.Gas),
		// NewToAddr: "",
	})
	if err != nil {
		return err
	}

	// 4)
	// 发送交易
	_, err = c.Client.SendTransaction(&SendTransactionReq{
		Data: resp2.Result,
	})
	if err != nil {
		return err
	}
	return nil
}

// DeployContractNote 部署合约备注信息
type DeployContractNote struct {
	ABI string `json:"abi"` // 合约 abi 内容
}

// DeployContract 部署合约
func DeployContract(chain Chain, contract Contract) (*DeployContractResp, error) {
	ec := NewClient(chain.ParaName, chain.EndPoint)

	// 1)
	// 构建部署备注信息
	n := &DeployContractNote{
		ABI: contract.ABI,
	}
	nb, err := json.Marshal(n)
	if err != nil {
		return nil, errors.Wrap(err, "json.Marshal err")
	}

	// 2)
	// 创建部署合约交易
	resp1, err := ec.CreateDeployTx(&CreateDeployTxReq{
		Code:     contract.BIN,
		Abi:      contract.ABI,
		ParaName: chain.ParaName,
		Note:     base64.StdEncoding.EncodeToString(nb),
	})
	if err != nil {
		return nil, errors.Wrap(err, "ec.CreateDeployTx err")
	}

	// 3)
	// 估算部署交易或者调用交易需要的 gas
	resp2, err := ec.EstimateGas(&EstimateGasReq{
		Tx:   resp1.Result,
		From: chain.CallAddr,
	})
	if err != nil {
		return nil, errors.Wrap(err, "ec.EstimateGas err")
	}

	// 4)
	// 签名
	resp3, err := ec.SignRawTx(&SignRawTxReq{
		Privkey: chain.CallPrivkey,
		TxHex:   resp1.Result,
		Expire:  chain.TxExpire,
		Fee:     convert.ToInt64(resp2.Gas),
	})
	if err != nil {
		return nil, errors.Wrap(err, "ec.SignRawTx err")
	}

	// 5)
	// 发送交易
	resp4, err := ec.SendTransaction(&SendTransactionReq{
		Data: resp3.Result,
	})
	if err != nil {
		return nil, errors.Wrap(err, "ec.SendTransaction err")
	}

	// 6)
	// 获取合约地址
	resp5, err := ec.CalcNewContractAddr(&CalcNewContractAddrReq{
		Caller: chain.CallAddr,
		TxHash: strings.TrimPrefix(resp4.Result, "0x"),
	})
	if err != nil {
		return nil, errors.Wrap(err, "ec.CalcNewContractAddr err")
	}

	var result DeployContractResp
	result.ContractAddr = resp5.Result
	result.TxHash = resp4.Result

	return &result, nil
}

// Client EVM 虚拟机客户端
type Client struct {
	paraName string // 平行链名称，目标节点不是平行链则为空
	c        xhttp.RPCClient
}

// NewClient 新建 EVM 虚拟机客户端
func NewClient(paraName, endpoint string, opts ...xhttp.RPCOption) *Client {
	return &Client{paraName: paraName, c: xhttp.NewRPCClient(endpoint, opts...)}
}

// GetTxFromHex 根据十六进制字符串获取交易信息
func GetTxFromHex(hex string) (*chainTypes.Transaction, error) {
	data, err := chainCommon.FromHex(hex)
	if err != nil {
		return nil, err
	}

	var t chainTypes.Transaction
	err = chainTypes.Decode(data, &t)
	if err != nil {
		return nil, err
	}

	return &t, nil
}

// CreateDeployTx 创建未签名的部署合约交易
// https://chain.33.cn/document/108#1.2%20%E5%88%9B%E5%BB%BA%E9%83%A8%E7%BD%B2%E5%90%88%E7%BA%A6%E4%BA%A4%E6%98%93%20CreateDeployTx
func (c *Client) CreateDeployTx(req *CreateDeployTxReq) (*CreateDeployTxResp, error) {
	if c.paraName != "" && req.ParaName == "" {
		req.ParaName = c.paraName
	}

	rpcResp, err := c.c.Call(MethodEVMCreateDeployTx, req)
	if err != nil {
		return nil, err
	}

	result, err := rpcResp.GetString()
	if err != nil {
		return nil, err
	}

	return &CreateDeployTxResp{Result: result}, nil
}

// CreateCallTx 创建操作合约交易
// https://chain.33.cn/document/108#1.3%20%E5%88%9B%E5%BB%BA%E8%B0%83%E7%94%A8%E5%90%88%E7%BA%A6%E4%BA%A4%E6%98%93%20CreateCallTx
func (c *Client) CreateCallTx(req *CreateCallTxReq) (*CreateCallTxResp, error) {
	if c.paraName != "" && req.ParaName == "" {
		req.ParaName = c.paraName
	}

	rpcResp, err := c.c.Call(MethodEVMCreateCallTx, req)
	if err != nil {
		return nil, err
	}

	result, err := rpcResp.GetString()
	if err != nil {
		return nil, err
	}

	return &CreateCallTxResp{Result: result}, nil
}

// CalcNewContractAddr 获取合约地址
// https://chain.33.cn/document/108#1.4%20%E8%8E%B7%E5%8F%96%E5%90%88%E7%BA%A6%E5%9C%B0%E5%9D%80%20CalcNewContractAddr
func (c *Client) CalcNewContractAddr(req *CalcNewContractAddrReq) (*CalcNewContractAddrResp, error) {
	req.TxHash = strings.TrimPrefix(req.TxHash, "0x")

	rpcResp, err := c.c.Call(MethodEVMCalcNewContractAddr, req)
	if err != nil {
		return nil, err
	}

	result, err := rpcResp.GetString()
	if err != nil {
		return nil, err
	}

	return &CalcNewContractAddrResp{Result: result}, nil
}

// EstimateGas 估算部署交易或者调用交易需要的 gas
// https://chain.33.cn/document/108#1.1%20%E4%BC%B0%E7%AE%97%E9%83%A8%E7%BD%B2%E4%BA%A4%E6%98%93%E6%88%96%E8%80%85%E8%B0%83%E7%94%A8%E4%BA%A4%E6%98%93%E9%9C%80%E8%A6%81%E7%9A%84gas
func (c *Client) EstimateGas(req *EstimateGasReq) (*EstimateGasResp, error) {
	req.Tx = strings.TrimPrefix(req.Tx, "0x")

	var resp EstimateGasResp
	err := c.c.CallFor(&resp, MethodChain33Query, &Chain33QueryReq{
		Execer:   c.paraName + ExecerEVM,
		FuncName: FuncNameEstimateGas,
		Payload:  req,
	})
	if err != nil {
		return nil, err
	}

	return &resp, nil
}

// CheckAddrExists 查询合约地址是否存在
// https://chain.33.cn/document/108#1.5%20%E6%9F%A5%E8%AF%A2%E5%90%88%E7%BA%A6%E5%9C%B0%E5%9D%80%E6%98%AF%E5%90%A6%E5%AD%98%E5%9C%A8%20CheckAddrExists
func (c *Client) CheckAddrExists(req *CheckAddrExistsReq) (*CheckAddrExistsResp, error) {
	var resp CheckAddrExistsResp
	err := c.c.CallFor(&resp, MethodChain33Query, &Chain33QueryReq{
		Execer:   c.paraName + ExecerEVM,
		FuncName: FuncNameCheckAddrExists,
		Payload:  req,
	})
	if err != nil {
		return nil, err
	}

	return &resp, nil
}

// GetPackData 获取合约打包信息
// https://chain.33.cn/document/108#1.6.1%20GetPackData
func (c *Client) GetPackData(req *GetPackDataReq) (*GetPackDataResp, error) {
	var resp GetPackDataResp
	err := c.c.CallFor(&resp, MethodChain33Query, &Chain33QueryReq{
		Execer:   c.paraName + ExecerEVM,
		FuncName: FuncNameGetPackData,
		Payload:  req,
	})
	if err != nil {
		return nil, err
	}

	return &resp, nil
}

// Query 查询合约信息
// https://chain.33.cn/document/108#1.6.2%20Query
func (c *Client) Query(req *QueryReq) (*QueryResp, error) {
	var resp QueryResp
	err := c.c.CallFor(&resp, MethodChain33Query, &Chain33QueryReq{
		Execer:   c.paraName + ExecerEVM,
		FuncName: FuncNameQuery,
		Payload:  req,
	})
	if err != nil {
		return nil, err
	}

	return &resp, nil
}

// GetUnpackData 获取合约解包信息
// https://chain.33.cn/document/108#1.6.3%20GetUnpackData
func (c *Client) GetUnpackData(req *GetUnpackDataReq) (*GetUnpackDataResp, error) {
	var resp GetUnpackDataResp
	err := c.c.CallFor(&resp, MethodChain33Query, &Chain33QueryReq{
		Execer:   c.paraName + ExecerEVM,
		FuncName: FuncNameGetUnpackData,
		Payload:  req,
	})
	if err != nil {
		return nil, err
	}

	return &resp, nil
}

// CreateRawTransaction 构造交易
// https://chain.33.cn/document/93#1.1.1%20%E6%9E%84%E9%80%A0%E4%BA%A4%E6%98%93%20CreateRawTransaction
func (c *Client) CreateRawTransaction(req *CreateRawTransactionReq) (*CreateRawTransactionResp, error) {
	rpcResp, err := c.c.Call(MethodChain33CreateRawTransaction, req)
	if err != nil {
		return nil, err
	}

	result, err := rpcResp.GetString()
	if err != nil {
		return nil, err
	}

	return &CreateRawTransactionResp{Result: result}, nil
}

// CreateNoBalanceTransaction 构造并发送不收手续费交易（平行链）
// https://chain.33.cn/document/93#1.1.4%20%E6%9E%84%E9%80%A0%E5%B9%B6%E5%8F%91%E9%80%81%E4%B8%8D%E6%94%B6%E6%89%8B%E7%BB%AD%E8%B4%B9%E4%BA%A4%E6%98%93%20CreateNoBalanceTransaction%EF%BC%88%E5%B9%B3%E8%A1%8C%E9%93%BE%EF%BC%89
func (c *Client) CreateNoBalanceTransaction(req *CreateNoBalanceTransactionReq) (*CreateNoBalanceTransactionResp, error) {
	rpcResp, err := c.c.Call(MethodChain33CreateNoBalanceTransaction, req)
	if err != nil {
		return nil, err
	}

	result, err := rpcResp.GetString()
	if err != nil {
		return nil, err
	}

	return &CreateNoBalanceTransactionResp{Result: result}, nil
}

// CreateRawTxGroup 构造交易组
// https://chain.33.cn/document/93#1.8%20%E6%9E%84%E9%80%A0%E4%BA%A4%E6%98%93%E7%BB%84%20CreateRawTxGroup
func (c *Client) CreateRawTxGroup(req *CreateRawTxGroupReq) (*CreateRawTxGroupResp, error) {
	rpcResp, err := c.c.Call(MethodChain33CreateRawTxGroup, req)
	if err != nil {
		return nil, err
	}

	result, err := rpcResp.GetString()
	if err != nil {
		return nil, err
	}

	return &CreateRawTxGroupResp{Result: result}, nil
}

// SignRawTx 交易签名
// https://chain.33.cn/document/93#1.1.2%20%E4%BA%A4%E6%98%93%E7%AD%BE%E5%90%8D%20SignRawTx
func (c *Client) SignRawTx(req *SignRawTxReq) (*SignRawTxResp, error) {
	rpcResp, err := c.c.Call(MethodChain33SignRawTx, req)
	if err != nil {
		return nil, err
	}

	result, err := rpcResp.GetString()
	if err != nil {
		return nil, err
	}

	return &SignRawTxResp{Result: result}, nil
}

// ReWriteRawTx 重写交易
// https://chain.33.cn/document/93#1.1.6%20%E9%87%8D%E5%86%99%E4%BA%A4%E6%98%93%20ReWriteRawTx
func (c *Client) ReWriteRawTx(req *ReWriteRawTxReq) (*ReWriteRawTxResp, error) {
	rpcResp, err := c.c.Call(MethodChain33ReWriteRawTx, req)
	if err != nil {
		return nil, err
	}

	result, err := rpcResp.GetString()
	if err != nil {
		return nil, err
	}

	return &ReWriteRawTxResp{Result: result}, nil
}

// SendTransaction 发送交易
// https://chain.33.cn/document/93#1.1.3%20%E5%8F%91%E9%80%81%E4%BA%A4%E6%98%93%20SendTransaction
func (c *Client) SendTransaction(req *SendTransactionReq) (*SendTransactionResp, error) {
	rpcResp, err := c.c.Call(MethodChain33SendTransaction, req)
	if err != nil {
		return nil, err
	}

	result, err := rpcResp.GetString()
	if err != nil {
		return nil, err
	}

	return &SendTransactionResp{Result: result}, nil
}

// QueryTransaction 根据哈希查询交易信息
// https://chain.33.cn/document/93#1.2%20%E6%A0%B9%E6%8D%AE%E5%93%88%E5%B8%8C%E6%9F%A5%E8%AF%A2%E4%BA%A4%E6%98%93%E4%BF%A1%E6%81%AF%20QueryTransaction
func (c *Client) QueryTransaction(req *QueryTransactionReq) (*QueryTransactionResp, error) {
	var resp QueryTransactionResp
	err := c.c.CallFor(&resp, MethodChain33QueryTransaction, req)
	if err != nil {
		return nil, err
	}

	return &resp, nil
}

// GetTxByHashes 根据哈希数组批量获取交易信息
// https://chain.33.cn/document/93#1.4%20%E6%A0%B9%E6%8D%AE%E5%93%88%E5%B8%8C%E6%95%B0%E7%BB%84%E6%89%B9%E9%87%8F%E8%8E%B7%E5%8F%96%E4%BA%A4%E6%98%93%E4%BF%A1%E6%81%AF%20GetTxByHashes
func (c *Client) GetTxByHashes(req *GetTxByHashesReq) (*GetTxByHashesResp, error) {
	var resp GetTxByHashesResp
	err := c.c.CallFor(&resp, MethodChain33GetTxByHashes, req)
	if err != nil {
		return nil, err
	}

	return &resp, nil
}

// GetVersion 获取区块链版本
// https://chain.33.cn/document/97#1.1%20%20%E8%8E%B7%E5%8F%96%E7%89%88%E6%9C%AC%20Version
func (c *Client) GetVersion() (*GetVersionResp, error) {
	var resp GetVersionResp
	err := c.c.CallFor(&resp, MethodChain33Version, &GetVersionReq{})
	if err != nil {
		return nil, err
	}

	return &resp, nil
}

// ContractQuery 合约查询，无需用户信息
// result为出参，若不为nil则使用该参数的解析方法解析，返回为nil。 若为nil则使用chain33-jrpc提供的接口解析。
func (c *ContractClient) ContractQuery(param string, result UnpackAble) ([]string, error) {
	// GetPackData
	resp, err := c.Client.GetPackData(&GetPackDataReq{
		Abi:       c.AbiJson,
		Parameter: param,
	})
	if err != nil {
		return nil, err
	}

	// Query
	resp1, err := c.Client.Query(&QueryReq{
		Address: c.ContractAddr,
		Input:   resp.PackData,
	})
	if err != nil {
		return nil, err
	}

	// GetUnpackData
	if result != nil {
		//r, _ := hexutil.Decode(resp.PackData[:10])
		//fmt.Println(string(r))
		fmt.Println(strconv.ParseInt(resp1.RawData[2:], 16, 64))
		return nil, result.Unpack(c.Client, resp.PackData[:10]+resp1.RawData[2:])
	}
	resp2, err := c.Client.GetUnpackData(&GetUnpackDataReq{
		Abi: c.AbiJson,
		//Parameter: param,
		Name: "balanceOf",
		Data: resp1.RawData,
	})
	if err != nil {
		return nil, err
	}

	return resp2.UnpackData, nil
}

// UnpackAble 可解析
type UnpackAble interface {
	Unpack(c *Client, data string) error
}

// CreateWithholdTransaction 创建代扣交易
/*func (c *Client) CreateWithholdTransaction(paraName string) (*chainTypes.Transaction, error) {
	if c.paraName != "" && paraName == "" {
		paraName = c.paraName
	}

	exec := paraName + chainTypes.NoneX
	tx := &chainTypes.Transaction{
		Execer:  []byte(exec),
		Payload: []byte("withhold-transaction"),
		Nonce:   rand.Int63(),
		To:      chainAddress.ExecAddress(exec),
	}

	return tx, nil
}*/

// CreateWithholdTxGroup 创建代扣交易组
/*func (c *Client) CreateWithholdTxGroup(paraName string, transactions ...*chainTypes.Transaction) (*chainTypes.Transactions, error) {
	if len(transactions) == 0 {
		return nil, errors.New("invalid transactions")
	}

	wt, err := c.CreateWithholdTransaction(paraName)
	if err != nil {
		return nil, err
	}
	txs := []*chainTypes.Transaction{wt}

	return chainTypes.CreateTxGroup(append(txs, transactions...), DefaultFeeRate)
}*/

// Chain 区块链详情
type Chain struct {
	ParaName    string `json:",default=user.p.testproofv2." validate:"required" label:"平行链名称"`                                                // 平行链名称
	EndPoint    string `json:",default=http://172.16.101.87:8901" validate:"required" label:"节点地址"`                                           // 节点地址
	CallAddr    string `json:",default=133AfuMYQXRxc45JGUb1jLk1M1W4ka39L1" validate:"required" label:"调用者地址"`                                 // 调用者地址
	CallPrivkey string `json:",default=0x85c6c95bcb41779f1d197e686d26b228a523fa36b77cfed79edb59b8853b569b" validate:"required" label:"调用者私钥"` // 调用者私钥
	TxExpire    string `json:",default=300s" validate:"required" label:"交易过期时间"`                                                              // 交易过期时间
}

// Contract 合约详情
type Contract struct {
	Type       string `json:",optional"`                                       // 合约类型
	Identifier string `json:",optional"`                                       // 合约标识符
	Address    string `json:",optional"`                                       // 合约地址
	ABI        string `json:",optional"`                                       // 合约 abi 内容
	BIN        string `json:",optional"`                                       // 合约 bin 内容
	ABIFile    string `json:"ABIFile" validate:"required" label:"合约 abi 文件名称"` // 合约 abi 文件名称，该文件请放置在 etc 目录中
	BINFile    string `json:"BINFile" validate:"required" label:"合约 bin 文件名称"` // 合约 bin 文件名称，该文件请放置在 etc 目录中
}

// DeployContractResp 部署合约响应
type DeployContractResp struct {
	ContractAddr string `json:"contract_addr"` // 合约地址
	TxHash       string `json:"tx_hash"`       // 交易 hash
}

const (
	// MethodChain33Query 请求方法：Chain33.Query
	MethodChain33Query = "Chain33.Query"
	// MethodChain33CreateRawTransaction 请求方法：Chain33.CreateRawTransaction
	MethodChain33CreateRawTransaction = "Chain33.CreateRawTransaction"
	// MethodChain33CreateNoBalanceTransaction 请求方法：Chain33.CreateNoBalanceTransaction
	MethodChain33CreateNoBalanceTransaction = "Chain33.CreateNoBalanceTransaction"
	// MethodChain33CreateRawTxGroup 请求方法：Chain33.CreateRawTxGroup
	MethodChain33CreateRawTxGroup = "Chain33.CreateRawTxGroup"
	// MethodChain33SignRawTx 请求方法：Chain33.SignRawTx
	MethodChain33SignRawTx = "Chain33.SignRawTx"
	// MethodChain33ReWriteRawTx 请求方法：Chain33.ReWriteRawTx
	MethodChain33ReWriteRawTx = "Chain33.ReWriteRawTx"
	// MethodChain33SendTransaction 请求方法：Chain33.SendTransaction
	MethodChain33SendTransaction = "Chain33.SendTransaction"
	// MethodChain33QueryTransaction 请求方法：Chain33.QueryTransaction
	MethodChain33QueryTransaction = "Chain33.QueryTransaction"
	// MethodChain33GetTxByHashes 请求方法：Chain33.GetTxByHashes
	MethodChain33GetTxByHashes = "Chain33.GetTxByHashes"
	// MethodChain33Version 请求方法：Chain33.Version
	MethodChain33Version = "Chain33.Version"

	// MethodEVMCreateDeployTx 请求方法：evm.CreateDeployTx
	MethodEVMCreateDeployTx = "evm.CreateDeployTx"
	// MethodEVMCalcNewContractAddr 请求方法：evm.CalcNewContractAddr
	MethodEVMCalcNewContractAddr = "evm.CalcNewContractAddr"
	// MethodEVMCreateCallTx 请求方法：evm.CreateCallTx
	MethodEVMCreateCallTx = "evm.CreateCallTx"

	// ExecerEVM 执行器：evm
	ExecerEVM = "evm"
	// DefaultFeeRate 默认手续费率
	DefaultFeeRate = 100000

	// TxFail 交易执行失败
	TxFail = 1
	// TxSuccess 交易执行成功
	TxSuccess = 2
	// ExecOk 执行器执行成功
	ExecOk = "ExecOk"

	// FuncNameEstimateGas 函数名称：EstimateGas
	FuncNameEstimateGas = "EstimateGas"
	// FuncNameCheckAddrExists 函数名称：CheckAddrExists
	FuncNameCheckAddrExists = "CheckAddrExists"
	// FuncNameGetPackData 函数名称：GetPackData
	FuncNameGetPackData = "GetPackData"
	// FuncNameQuery 函数名称：Query
	FuncNameQuery = "Query"
	// FuncNameGetUnpackData 函数名称：GetUnpackData
	FuncNameGetUnpackData = "GetUnpackData"
)

// CreateDeployTxReq 创建未签名的部署合约交易请求
type CreateDeployTxReq struct {
	Code      string `json:"code"`      // 部署合约的 bin 内容
	Abi       string `json:"abi"`       // 部署合约的 abi 内容
	Fee       int64  `json:"fee"`       // 手续费必须大于等于 100000000
	Note      string `json:"note"`      // 非必填
	Alias     string `json:"alias"`     // 合约别名
	Parameter string `json:"parameter"` // 部署合约的参数 “constructor(zbc, zbc, 3300, ‘${evmCreatorAddr}’)” 原型为 constructor (string memory name, string memory symbol_,uint256 supply, address owner)，这里表示部署一个名称和 symbol 都为 zbc，总金额 3300*le8，拥有者为 evmCreatorAddr 的 ERC20 合约
	Expire    string `json:"expire"`    // 非必填
	ParaName  string `json:"paraName"`  // 如果是平行链参数 paraName 的值为 user.p.para. 如果是主链则为空
	Amount    int64  `json:"amount"`    // 非必填
}

// CreateDeployTxResp 创建未签名的部署合约交易响应
type CreateDeployTxResp struct {
	Result string `json:"result"` // 创建的交易数据
}

// CreateCallTxReq 创建操作合约交易请求
type CreateCallTxReq struct {
	Abi          string `json:"abi"`          // 部署合约的 abi 内容
	Fee          int64  `json:"fee"`          // 精确的手续费可以通过 EstimateGas 这个 jrpc 接口进行估算，同时该交易费需要满足根据部署交易体积大小计算出来的交易费要求，一般调用交易的交易费直接设置为通过交易体积大小计算出来的交易费即可
	Note         string `json:"note"`         // 非必填
	Parameter    string `json:"parameter"`    // 操作合约的参数，例如转账交易 “transfer(‘${evm_transferAddr}’, 20)”
	Expire       string `json:"expire"`       // 非必填
	ParaName     string `json:"paraName"`     // 如果是平行链参数 paraName 的值为 user.p.para. 如果是主链则为空
	ContractAddr string `json:"contractAddr"` // 合约地址
	Amount       int64  `json:"amount"`       // 非必填
}

// CreateCallTxResp 创建操作合约交易响应
type CreateCallTxResp struct {
	Result string `json:"result"` // 创建的交易数据
}

// CalcNewContractAddrReq 获取合约地址请求
type CalcNewContractAddrReq struct {
	Caller string `json:"caller"` // 部署合约的地址
	TxHash string `json:"txhash"` // 创建合约的交易哈希，去掉前面的 0x
}

// CalcNewContractAddrResp 获取合约地址响应
type CalcNewContractAddrResp struct {
	Result string `json:"result"` // 合约地址
}

// Chain33QueryReq 区块链查询请求
type Chain33QueryReq struct {
	Execer   string      `json:"execer"`   // 执行器名称，这里固定为 evm，如果是在平行链上则需要加上前缀，比如 user.p.game.evm
	FuncName string      `json:"funcName"` // 操作名称
	Payload  interface{} `json:"payload"`  // 载荷
}

// EstimateGasReq 估算部署交易或者调用交易需要的 gas 请求
type EstimateGasReq struct {
	Tx   string `json:"tx"`   // 部署合约交易或者调用合约交易的序列化后的字符串
	From string `json:"from"` // 合约交易调用者地址
}

// EstimateGasResp 估算部署交易或者调用交易需要的 gas 响应
type EstimateGasResp struct {
	Gas string `json:"gas"` // 估算需要的 gas 数值
}

// CheckAddrExistsReq 查询合约地址是否存在请求
type CheckAddrExistsReq struct {
	Addr string `json:"addr"` // 被查询的合约地址
}

// CheckAddrExistsResp 查询合约地址是否存在响应
type CheckAddrExistsResp struct {
	Contract     bool   `json:"contract"`     // 合约地址是否存在（0:不存在 1:存在）
	ContractAddr string `json:"contractAddr"` // 合约地址
	ContractName string `json:"contractName"` // 合约名称
	AliasName    string `json:"aliasName"`    // 合约别名
}

// GetPackDataReq 获取合约打包信息请求
type GetPackDataReq struct {
	Abi       string `json:"abi"`       // 合约 abi
	Parameter string `json:"parameter"` // 查询的参数信息
}

// GetPackDataResp 获取合约打包信息响应
type GetPackDataResp struct {
	PackData string `json:"packData"` // 需要查询的信息 pack 后的数据
}

// QueryReq 查询合约信息请求
type QueryReq struct {
	Address string `json:"address"` // 合约地址
	Input   string `json:"input"`   // 需要查询的信息 pack 后的数据
	Caller  string `json:"caller"`  // 合约部署者地址
}

// QueryResp 查询合约信息响应
type QueryResp struct {
	Address  string `json:"address"`  // 合约地址
	Input    string `json:"input"`    // 需要查询的信息 pack 后的数据
	Caller   string `json:"caller"`   // 合约部署者地址
	RawData  string `json:"rawData"`  // 查询到的结果
	JSONData string `json:"jsonData"` // json 数据
}

// GetUnpackDataReq 获取合约解包信息请求
type GetUnpackDataReq struct {
	Abi string `json:"abi"` // 合约 abi
	//Parameter string `json:"parameter"` // 查询的参数信息
	Name string `json:"name"`
	Data string `json:"data"` // 需要 unpack 的数据
}

// GetUnpackDataResp 获取合约解包信息响应
type GetUnpackDataResp struct {
	UnpackData []string `json:"unpackData"` // unpack 的数据
}

// CreateRawTransactionReq 构造交易请求
type CreateRawTransactionReq struct {
	To          string `json:"to"`          // 发送到地址
	Amount      int64  `json:"amount"`      // 发送金额，注意基础货币单位为 10^8
	Fee         int64  `json:"fee"`         // 手续费，注意基础货币单位为 10^8
	Note        string `json:"note"`        // 备注
	IsWithdraw  bool   `json:"isWithdraw"`  // 是否是token类型的转账（非 token 转账这个不用填 包括平行链的基础代币转账也不用填）
	IsToken     bool   `json:"isToken"`     // 是否为取款交易
	TokenSymbol string `json:"tokenSymbol"` // token 的 symbol（非 token 转账这个不用填）
	ExecName    string `json:"execName"`    // TransferToExec（转到合约）或 Withdraw（从合约中提款），如果要构造平行链上的转账或普通转账，此参数置空
	Execer      string `json:"execer"`      // 执行器名称，如果是普通转账，此处应填 coins，如果是构造平行链的基础代币，此处要填写 user.p.xxx.coins
}

// CreateRawTransactionResp 构造交易响应
type CreateRawTransactionResp struct {
	Result string `json:"result"` // 交易对象的十六进制字符串编码
}

// CreateNoBalanceTransactionReq 构造并发送不收手续费交易（平行链）请求
type CreateNoBalanceTransactionReq struct {
	TxHash  string `json:"txHex"`   // 未签名的原始交易数据
	PayAddr string `json:"payAddr"` // 用于付费的地址，这个地址要在主链上存在，并且里面有比特元用于支付手续费, payAddr 与 privkey 可以只输入其一，如果使用 payAddr 则依赖钱包中存储的私钥签名
	Privkey string `json:"privkey"` // 对应于 payAddr 的私钥。如果 payAddr 已经导入到平行链，可以只传地址
	Expire  string `json:"expire"`  // 过期时间可输入如 ”300s”，”-1.5h” 或者 ”2h45m” 的字符串，有效时间单位为 ”ns”, “us” (or “µs”), “ms”, “s”, “m”, “h”，不传递默认设置永不过期
}

// CreateNoBalanceTransactionResp 构造并发送不收手续费交易（平行链）响应
type CreateNoBalanceTransactionResp struct {
	Result string `json:"result"` // 未签名的原始交易数据
}

// CreateRawTxGroupReq 构造交易组请求
type CreateRawTxGroupReq struct {
	Txs []string `json:"txs"` // 十六进制格式交易数组
}

// CreateRawTxGroupResp 构造交易组响应
type CreateRawTxGroupResp struct {
	Result string `json:"result"` // 交易组对象的十六进制字符串
}

// SignRawTxReq 交易签名请求
type SignRawTxReq struct {
	Addr      string `json:"addr"`      // addr 与 privkey 可以只输入其一，如果使用 addr 则依赖钱包中存储的私钥签名
	Privkey   string `json:"privkey"`   // addr 与 privkey 可以只输入其一，如果使用 privkey 则直接签名
	TxHex     string `json:"txHex"`     // 上一步生成的原始交易数据
	Expire    string `json:"expire"`    // 过期时间可输入如 ”300s”，”-1.5h” 或者 ”2h45m” 的字符串，有效时间单位为 ”ns”, “us” (or “µs”), “ms”, “s”, “m”, “h”
	Index     int64  `json:"index"`     // 若是签名交易组，则为要签名的交易序号，从 1 开始，小于等于 0 则为签名组内全部交易
	Token     string `json:"token"`     // 非必填
	Fee       int64  `json:"fee"`       // 费用
	NewToAddr string `json:"newToAddr"` // 非必填
}

// SignRawTxResp 交易签名响应
type SignRawTxResp struct {
	Result string `json:"result"` // 交易签名后的十六进制字符串
}

// ReWriteRawTxReq 重写交易请求
type ReWriteRawTxReq struct {
	To     string `json:"to"`     // 重写交易的目的地址，只有单笔交易生效，交易组不生效
	Fee    int64  `json:"fee"`    // 重写交易的费用，交易组只会修改第一笔交易的费用
	Tx     string `json:"tx"`     // 需要重写的原始交易数据
	Expire string `json:"expire"` // 过期时间可输入如 ”300s”，”-1.5h” 或者 ”2h45m” 的字符串，有效时间单位为 ”ns”, “us” (or “µs”), “ms”, “s”, “m”, “h”
	Index  int64  `json:"index"`  // 若是交易组，则为要重写的交易序号，从1开始，小于等于0则为交易组内全部交易
}

// ReWriteRawTxResp 重写交易响应
type ReWriteRawTxResp struct {
	Result string `json:"result"` // 重写之后交易的十六进制字符串
}

// SendTransactionReq 发送交易请求
type SendTransactionReq struct {
	Data  string `json:"data"`  // 为上一步签名后的交易数据
	Token string `json:"token"` // 非必填，可为空
}

// SendTransactionResp 发送交易响应
type SendTransactionResp struct {
	Result string `json:"result"` // 交易发送后，生成的交易哈希（后面可以使用此哈希查询交易状态和历史）
}

// QueryTransactionReq 根据哈希查询交易信息请求
type QueryTransactionReq struct {
	Hash string `json:"hash"` // 交易哈希
}

// QueryTransactionResp 根据哈希查询交易信息响应
type QueryTransactionResp struct {
	Transaction
}

// GetTxByHashesReq 根据哈希数组批量获取交易信息请求
type GetTxByHashesReq struct {
	Hashes        []string `json:"hashes"`        // 交易哈希数字
	DisableDetail bool     `json:"disableDetail"` // 是否隐藏交易详情，默认为 false
}

// GetTxByHashesResp 根据哈希数组批量获取交易信息响应
type GetTxByHashesResp struct {
	Txs []*Transaction `json:"txs"` // 交易详情列表
}

// GetVersionReq 获取区块链版本请求
type GetVersionReq struct{}

// GetVersionResp 获取区块链版本响应
type GetVersionResp struct {
	Title   string `json:"title"`   // 区块链名，该节点 chain33.toml 中配置的 title 值
	App     string `json:"app"`     // 应用 app 的版本
	Chain33 string `json:"chain33"` // 版本信息，版本号-GitCommit（前八个字符）
	LocalDB string `json:"localDb"` // localdb 版本号
}

// Transaction 交易详情信息
type Transaction struct {
	Tx         *Tx        `json:"tx"`
	Receipt    *Receipt   `json:"receipt"`
	Proofs     []string   `json:"proofs"`
	Height     int64      `json:"height"`
	Index      int64      `json:"index"`
	BlockTime  int64      `json:"blockTime"`
	Amount     int64      `json:"amount"`
	FromAddr   string     `json:"fromAddr"`
	ActionName string     `json:"actionName"`
	Assets     []*Asset   `json:"assets"`
	TxProofs   []*TxProof `json:"txProofs"`
	FullHash   string     `json:"fullHash"`
}

// Signature 签名信息
type Signature struct {
	Ty        int32  `json:"ty"`
	Pubkey    string `json:"pubkey"`
	Signature string `json:"signature"`
}

// Tx 交易信息
type Tx struct {
	Execer     string          `json:"execer"`
	Payload    json.RawMessage `json:"payload"`
	RawPayload string          `json:"rawPayload"`
	Signature  *Signature      `json:"signature"`
	Fee        int64           `json:"fee"`
	FeeFmt     string          `json:"feefmt"`
	Expire     int64           `json:"expire"`
	Nonce      int64           `json:"nonce"`
	From       string          `json:"from,omitempty"`
	To         string          `json:"to"`
	Amount     int64           `json:"amount,omitempty"`
	AmountFmt  string          `json:"amountfmt,omitempty"`
	GroupCount int32           `json:"groupCount,omitempty"`
	Header     string          `json:"header,omitempty"`
	Next       string          `json:"next,omitempty"`
	Hash       string          `json:"hash,omitempty"`
	ChainID    int32           `json:"chainID,omitempty"`
}

// Receipt 收据信息
type Receipt struct {
	Ty     int32         `json:"ty"`
	TyName string        `json:"tyName"`
	Logs   []*ReceiptLog `json:"logs"`
}

// ReceiptLog 收据日志信息
type ReceiptLog struct {
	Ty     int32           `json:"ty"`
	TyName string          `json:"tyName"`
	Log    json.RawMessage `json:"log"`
	RawLog string          `json:"rawLog"`
}

// Asset 资产信息
type Asset struct {
	Exec   string `json:"exec"`
	Symbol string `json:"symbol"`
	Amount int64  `json:"amount"`
}

// TxProof 交易存证信息
type TxProof struct {
	Proofs   []string `json:"proofs"`
	Index    uint32   `json:"index"`
	RootHash string   `json:"rootHash"`
}
