mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-27 03:58:06 +00:00
Merge pull request #747 from nspcc-dev/fix-contract-deploy-gas-parameter
Use wallet in smartcontract deploy/invoke commands and calculate GAS properly
This commit is contained in:
commit
734778c1f9
5 changed files with 120 additions and 59 deletions
|
@ -10,26 +10,31 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"github.com/go-yaml/yaml"
|
"github.com/go-yaml/yaml"
|
||||||
|
"github.com/nspcc-dev/neo-go/cli/flags"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpc/client"
|
"github.com/nspcc-dev/neo-go/pkg/rpc/client"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpc/request"
|
"github.com/nspcc-dev/neo-go/pkg/rpc/request"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
|
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errNoEndpoint = errors.New("no RPC endpoint specified, use option '--endpoint' or '-e'")
|
errNoEndpoint = errors.New("no RPC endpoint specified, use option '--endpoint' or '-e'")
|
||||||
errNoInput = errors.New("no input file was found, specify an input file with the '--in or -i' flag")
|
errNoInput = errors.New("no input file was found, specify an input file with the '--in or -i' flag")
|
||||||
errNoConfFile = errors.New("no config file was found, specify a config file with the '--config' or '-c' flag")
|
errNoConfFile = errors.New("no config file was found, specify a config file with the '--config' or '-c' flag")
|
||||||
errNoWIF = errors.New("no WIF parameter found, specify it with the '--wif or -w' flag")
|
errNoWallet = errors.New("no wallet parameter found, specify it with the '--wallet or -w' flag")
|
||||||
errNoScriptHash = errors.New("no smart contract hash was provided, specify one as the first argument")
|
errNoScriptHash = errors.New("no smart contract hash was provided, specify one as the first argument")
|
||||||
errNoSmartContractName = errors.New("no name was provided, specify the '--name or -n' flag")
|
errNoSmartContractName = errors.New("no name was provided, specify the '--name or -n' flag")
|
||||||
errFileExist = errors.New("A file with given smart-contract name already exists")
|
errFileExist = errors.New("A file with given smart-contract name already exists")
|
||||||
|
@ -38,13 +43,17 @@ var (
|
||||||
Name: "endpoint, e",
|
Name: "endpoint, e",
|
||||||
Usage: "trusted RPC endpoint address (like 'http://localhost:20331')",
|
Usage: "trusted RPC endpoint address (like 'http://localhost:20331')",
|
||||||
}
|
}
|
||||||
wifFlag = cli.StringFlag{
|
walletFlag = cli.StringFlag{
|
||||||
Name: "wif, w",
|
Name: "wallet, w",
|
||||||
Usage: "key to sign deployed transaction (in wif format)",
|
Usage: "wallet to use to get the key for transaction signing",
|
||||||
|
}
|
||||||
|
addressFlag = flags.AddressFlag{
|
||||||
|
Name: "address, a",
|
||||||
|
Usage: "address to use as transaction signee (and gas source)",
|
||||||
}
|
}
|
||||||
gasFlag = cli.Float64Flag{
|
gasFlag = cli.Float64Flag{
|
||||||
Name: "gas, g",
|
Name: "gas, g",
|
||||||
Usage: "gas to pay for transaction",
|
Usage: "gas to add to the transaction",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -86,8 +95,14 @@ func NewCommands() []cli.Command {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "deploy",
|
Name: "deploy",
|
||||||
Usage: "deploy a smart contract (.avm with description)",
|
Usage: "deploy a smart contract (.avm with description)",
|
||||||
|
Description: `Deploys given contract into the chain. The gas parameter is for additional
|
||||||
|
gas to be added as a network fee to prioritize the transaction. It may also
|
||||||
|
be required to add that to satisfy chain's policy regarding transaction size
|
||||||
|
and the minimum size fee (so if transaction send fails, try adding 0.001 GAS
|
||||||
|
to it).
|
||||||
|
`,
|
||||||
Action: contractDeploy,
|
Action: contractDeploy,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
|
@ -99,14 +114,15 @@ func NewCommands() []cli.Command {
|
||||||
Usage: "configuration input file (*.yml)",
|
Usage: "configuration input file (*.yml)",
|
||||||
},
|
},
|
||||||
endpointFlag,
|
endpointFlag,
|
||||||
wifFlag,
|
walletFlag,
|
||||||
|
addressFlag,
|
||||||
gasFlag,
|
gasFlag,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "invoke",
|
Name: "invoke",
|
||||||
Usage: "invoke deployed contract on the blockchain",
|
Usage: "invoke deployed contract on the blockchain",
|
||||||
UsageText: "neo-go contract invoke -e endpoint -w wif [-g gas] scripthash [arguments...]",
|
UsageText: "neo-go contract invoke -e endpoint -w wallet [-a address] [-g gas] scripthash [arguments...]",
|
||||||
Description: `Executes given (as a script hash) deployed script with the given arguments.
|
Description: `Executes given (as a script hash) deployed script with the given arguments.
|
||||||
See testinvoke documentation for the details about parameters. It differs
|
See testinvoke documentation for the details about parameters. It differs
|
||||||
from testinvoke in that this command sends an invocation transaction to
|
from testinvoke in that this command sends an invocation transaction to
|
||||||
|
@ -115,14 +131,15 @@ func NewCommands() []cli.Command {
|
||||||
Action: invoke,
|
Action: invoke,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
endpointFlag,
|
endpointFlag,
|
||||||
wifFlag,
|
walletFlag,
|
||||||
|
addressFlag,
|
||||||
gasFlag,
|
gasFlag,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "invokefunction",
|
Name: "invokefunction",
|
||||||
Usage: "invoke deployed contract on the blockchain",
|
Usage: "invoke deployed contract on the blockchain",
|
||||||
UsageText: "neo-go contract invokefunction -e endpoint -w wif [-g gas] scripthash [method] [arguments...]",
|
UsageText: "neo-go contract invokefunction -e endpoint -w wallet [-a address] [-g gas] scripthash [method] [arguments...]",
|
||||||
Description: `Executes given (as a script hash) deployed script with the given method and
|
Description: `Executes given (as a script hash) deployed script with the given method and
|
||||||
and arguments. See testinvokefunction documentation for the details about
|
and arguments. See testinvokefunction documentation for the details about
|
||||||
parameters. It differs from testinvokefunction in that this command sends an
|
parameters. It differs from testinvokefunction in that this command sends an
|
||||||
|
@ -131,7 +148,8 @@ func NewCommands() []cli.Command {
|
||||||
Action: invokeFunction,
|
Action: invokeFunction,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
endpointFlag,
|
endpointFlag,
|
||||||
wifFlag,
|
walletFlag,
|
||||||
|
addressFlag,
|
||||||
gasFlag,
|
gasFlag,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -365,7 +383,7 @@ func invokeInternal(ctx *cli.Context, withMethod bool, signAndPush bool) error {
|
||||||
params = make([]smartcontract.Parameter, 0)
|
params = make([]smartcontract.Parameter, 0)
|
||||||
paramsStart = 1
|
paramsStart = 1
|
||||||
resp *result.Invoke
|
resp *result.Invoke
|
||||||
wif *keys.WIF
|
acc *wallet.Account
|
||||||
)
|
)
|
||||||
|
|
||||||
endpoint := ctx.String("endpoint")
|
endpoint := ctx.String("endpoint")
|
||||||
|
@ -394,8 +412,7 @@ func invokeInternal(ctx *cli.Context, withMethod bool, signAndPush bool) error {
|
||||||
|
|
||||||
if signAndPush {
|
if signAndPush {
|
||||||
gas = util.Fixed8FromFloat(ctx.Float64("gas"))
|
gas = util.Fixed8FromFloat(ctx.Float64("gas"))
|
||||||
|
acc, err = getAccFromContext(ctx)
|
||||||
wif, err = getWifFromContext(ctx)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -421,7 +438,7 @@ func invokeInternal(ctx *cli.Context, withMethod bool, signAndPush bool) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(fmt.Errorf("bad script returned from the RPC node: %v", err), 1)
|
return cli.NewExitError(fmt.Errorf("bad script returned from the RPC node: %v", err), 1)
|
||||||
}
|
}
|
||||||
txHash, err := c.SignAndPushInvocationTx(script, wif, gas)
|
txHash, err := c.SignAndPushInvocationTx(script, acc, 0, gas)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(fmt.Errorf("failed to push invocation tx: %v", err), 1)
|
return cli.NewExitError(fmt.Errorf("failed to push invocation tx: %v", err), 1)
|
||||||
}
|
}
|
||||||
|
@ -525,17 +542,41 @@ func inspect(ctx *cli.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getWifFromContext(ctx *cli.Context) (*keys.WIF, error) {
|
func getAccFromContext(ctx *cli.Context) (*wallet.Account, error) {
|
||||||
wifStr := ctx.String("wif")
|
var addr util.Uint160
|
||||||
if len(wifStr) == 0 {
|
|
||||||
return nil, cli.NewExitError(errNoWIF, 1)
|
wPath := ctx.String("wallet")
|
||||||
|
if len(wPath) == 0 {
|
||||||
|
return nil, cli.NewExitError(errNoWallet, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
wif, err := keys.WIFDecode(wifStr, 0)
|
wall, err := wallet.NewWalletFromFile(wPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, cli.NewExitError(fmt.Errorf("bad wif: %v", err), 1)
|
return nil, cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
return wif, nil
|
addrFlag := ctx.Generic("address").(*flags.Address)
|
||||||
|
if addrFlag.IsSet {
|
||||||
|
addr = addrFlag.Uint160()
|
||||||
|
} else {
|
||||||
|
addr = wall.GetChangeAddress()
|
||||||
|
}
|
||||||
|
acc := wall.GetAccount(addr)
|
||||||
|
if acc == nil {
|
||||||
|
return nil, cli.NewExitError(fmt.Errorf("wallet contains no account for '%s'", address.Uint160ToString(addr)), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Enter account %s password > ", address.Uint160ToString(addr))
|
||||||
|
rawPass, err := terminal.ReadPassword(syscall.Stdin)
|
||||||
|
fmt.Println()
|
||||||
|
if err != nil {
|
||||||
|
return nil, cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
pass := strings.TrimRight(string(rawPass), "\n")
|
||||||
|
err = acc.Decrypt(pass)
|
||||||
|
if err != nil {
|
||||||
|
return nil, cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
return acc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// contractDeploy deploys contract.
|
// contractDeploy deploys contract.
|
||||||
|
@ -554,7 +595,7 @@ func contractDeploy(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
gas := util.Fixed8FromFloat(ctx.Float64("gas"))
|
gas := util.Fixed8FromFloat(ctx.Float64("gas"))
|
||||||
|
|
||||||
wif, err := getWifFromContext(ctx)
|
acc, err := getAccFromContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -583,7 +624,9 @@ func contractDeploy(ctx *cli.Context) error {
|
||||||
return cli.NewExitError(fmt.Errorf("failed to create deployment script: %v", err), 1)
|
return cli.NewExitError(fmt.Errorf("failed to create deployment script: %v", err), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
txHash, err := c.SignAndPushInvocationTx(txScript, wif, gas)
|
sysfee := smartcontract.GetDeploymentPrice(request.DetailsToSCProperties(&conf.Contract))
|
||||||
|
|
||||||
|
txHash, err := c.SignAndPushInvocationTx(txScript, acc, sysfee, gas)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(fmt.Errorf("failed to push invocation tx: %v", err), 1)
|
return cli.NewExitError(fmt.Errorf("failed to push invocation tx: %v", err), 1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,18 +93,7 @@ func getSyscallPrice(v *vm.VM, id uint32) util.Fixed8 {
|
||||||
arg := estack.Peek(1).BigInt().Int64()
|
arg := estack.Peek(1).BigInt().Int64()
|
||||||
return util.Fixed8FromInt64(arg * 5000)
|
return util.Fixed8FromInt64(arg * 5000)
|
||||||
case neoContractCreate, neoContractMigrate, antSharesContractCreate, antSharesContractMigrate:
|
case neoContractCreate, neoContractMigrate, antSharesContractCreate, antSharesContractMigrate:
|
||||||
fee := int64(100)
|
return smartcontract.GetDeploymentPrice(smartcontract.PropertyState(estack.Peek(3).BigInt().Int64()))
|
||||||
props := smartcontract.PropertyState(estack.Peek(3).BigInt().Int64())
|
|
||||||
|
|
||||||
if props&smartcontract.HasStorage != 0 {
|
|
||||||
fee += 400
|
|
||||||
}
|
|
||||||
|
|
||||||
if props&smartcontract.HasDynamicInvoke != 0 {
|
|
||||||
fee += 500
|
|
||||||
}
|
|
||||||
|
|
||||||
return util.Fixed8FromInt64(fee)
|
|
||||||
case systemStoragePut, systemStoragePutEx, neoStoragePut, antSharesStoragePut:
|
case systemStoragePut, systemStoragePutEx, neoStoragePut, antSharesStoragePut:
|
||||||
// price for storage PUT is 1 GAS per 1 KiB
|
// price for storage PUT is 1 GAS per 1 KiB
|
||||||
keySize := len(estack.Peek(1).Bytes())
|
keySize := len(estack.Peek(1).Bytes())
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core"
|
"github.com/nspcc-dev/neo-go/pkg/core"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpc/request"
|
"github.com/nspcc-dev/neo-go/pkg/rpc/request"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
|
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
|
||||||
|
@ -499,24 +499,30 @@ func (c *Client) TransferAsset(asset util.Uint256, address string, amount util.F
|
||||||
// SignAndPushInvocationTx signs and pushes given script as an invocation
|
// SignAndPushInvocationTx signs and pushes given script as an invocation
|
||||||
// transaction using given wif to sign it and spending the amount of gas
|
// transaction using given wif to sign it and spending the amount of gas
|
||||||
// specified. It returns a hash of the invocation transaction and an error.
|
// specified. It returns a hash of the invocation transaction and an error.
|
||||||
func (c *Client) SignAndPushInvocationTx(script []byte, wif *keys.WIF, gas util.Fixed8) (util.Uint256, error) {
|
func (c *Client) SignAndPushInvocationTx(script []byte, acc *wallet.Account, sysfee util.Fixed8, netfee util.Fixed8) (util.Uint256, error) {
|
||||||
var txHash util.Uint256
|
var txHash util.Uint256
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
tx := transaction.NewInvocationTX(script, gas)
|
tx := transaction.NewInvocationTX(script, sysfee)
|
||||||
|
gas := sysfee + netfee
|
||||||
fromAddress := wif.PrivateKey.Address()
|
|
||||||
|
|
||||||
if gas > 0 {
|
if gas > 0 {
|
||||||
if err = request.AddInputsAndUnspentsToTx(tx, fromAddress, core.UtilityTokenID(), gas, c); err != nil {
|
if err = request.AddInputsAndUnspentsToTx(tx, acc.Address, core.UtilityTokenID(), gas, c); err != nil {
|
||||||
return txHash, errors.Wrap(err, "failed to add inputs and unspents to transaction")
|
return txHash, errors.Wrap(err, "failed to add inputs and unspents to transaction")
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
addr, err := address.StringToUint160(acc.Address)
|
||||||
|
if err != nil {
|
||||||
|
return txHash, errors.Wrap(err, "failed to get address")
|
||||||
|
}
|
||||||
|
tx.Attributes = append(tx.Attributes,
|
||||||
|
transaction.Attribute{
|
||||||
|
Usage: transaction.Script,
|
||||||
|
Data: addr.BytesBE(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
acc, err := wallet.NewAccountFromWIF(wif.S)
|
if err = acc.SignTx(tx); err != nil {
|
||||||
if err != nil {
|
|
||||||
return txHash, err
|
|
||||||
} else if err = acc.SignTx(tx); err != nil {
|
|
||||||
return txHash, errors.Wrap(err, "failed to sign tx")
|
return txHash, errors.Wrap(err, "failed to sign tx")
|
||||||
}
|
}
|
||||||
txHash = tx.Hash()
|
txHash = tx.Hash()
|
||||||
|
|
|
@ -80,17 +80,10 @@ func AddInputsAndUnspentsToTx(tx *transaction.Transaction, addr string, assetID
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateDeploymentScript returns a script that deploys given smart contract
|
// DetailsToSCProperties extract the fields needed from ContractDetails
|
||||||
// with its metadata.
|
// and converts them to smartcontract.PropertyState.
|
||||||
func CreateDeploymentScript(avm []byte, contract *ContractDetails) ([]byte, error) {
|
func DetailsToSCProperties(contract *ContractDetails) smartcontract.PropertyState {
|
||||||
var props smartcontract.PropertyState
|
var props smartcontract.PropertyState
|
||||||
|
|
||||||
script := io.NewBufBinWriter()
|
|
||||||
emit.Bytes(script.BinWriter, []byte(contract.Description))
|
|
||||||
emit.Bytes(script.BinWriter, []byte(contract.Email))
|
|
||||||
emit.Bytes(script.BinWriter, []byte(contract.Author))
|
|
||||||
emit.Bytes(script.BinWriter, []byte(contract.Version))
|
|
||||||
emit.Bytes(script.BinWriter, []byte(contract.ProjectName))
|
|
||||||
if contract.HasStorage {
|
if contract.HasStorage {
|
||||||
props |= smartcontract.HasStorage
|
props |= smartcontract.HasStorage
|
||||||
}
|
}
|
||||||
|
@ -100,7 +93,19 @@ func CreateDeploymentScript(avm []byte, contract *ContractDetails) ([]byte, erro
|
||||||
if contract.IsPayable {
|
if contract.IsPayable {
|
||||||
props |= smartcontract.IsPayable
|
props |= smartcontract.IsPayable
|
||||||
}
|
}
|
||||||
emit.Int(script.BinWriter, int64(props))
|
return props
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateDeploymentScript returns a script that deploys given smart contract
|
||||||
|
// with its metadata.
|
||||||
|
func CreateDeploymentScript(avm []byte, contract *ContractDetails) ([]byte, error) {
|
||||||
|
script := io.NewBufBinWriter()
|
||||||
|
emit.Bytes(script.BinWriter, []byte(contract.Description))
|
||||||
|
emit.Bytes(script.BinWriter, []byte(contract.Email))
|
||||||
|
emit.Bytes(script.BinWriter, []byte(contract.Author))
|
||||||
|
emit.Bytes(script.BinWriter, []byte(contract.Version))
|
||||||
|
emit.Bytes(script.BinWriter, []byte(contract.ProjectName))
|
||||||
|
emit.Int(script.BinWriter, int64(DetailsToSCProperties(contract)))
|
||||||
emit.Int(script.BinWriter, int64(contract.ReturnType))
|
emit.Int(script.BinWriter, int64(contract.ReturnType))
|
||||||
params := make([]byte, len(contract.Parameters))
|
params := make([]byte, len(contract.Parameters))
|
||||||
for k := range contract.Parameters {
|
for k := range contract.Parameters {
|
||||||
|
|
18
pkg/smartcontract/deployment_price.go
Normal file
18
pkg/smartcontract/deployment_price.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package smartcontract
|
||||||
|
|
||||||
|
import "github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
|
||||||
|
// GetDeploymentPrice returns contract deployment price based on its properties.
|
||||||
|
func GetDeploymentPrice(props PropertyState) util.Fixed8 {
|
||||||
|
fee := int64(100)
|
||||||
|
|
||||||
|
if props&HasStorage != 0 {
|
||||||
|
fee += 400
|
||||||
|
}
|
||||||
|
|
||||||
|
if props&HasDynamicInvoke != 0 {
|
||||||
|
fee += 500
|
||||||
|
}
|
||||||
|
|
||||||
|
return util.Fixed8FromInt64(fee)
|
||||||
|
}
|
Loading…
Reference in a new issue