diff --git a/cli/smartcontract/smart_contract.go b/cli/smartcontract/smart_contract.go
index c4b9ac7c9..accac7614 100644
--- a/cli/smartcontract/smart_contract.go
+++ b/cli/smartcontract/smart_contract.go
@@ -14,6 +14,7 @@ import (
 	"github.com/CityOfZion/neo-go/pkg/crypto/hash"
 	"github.com/CityOfZion/neo-go/pkg/crypto/keys"
 	"github.com/CityOfZion/neo-go/pkg/rpc"
+	"github.com/CityOfZion/neo-go/pkg/smartcontract"
 	"github.com/CityOfZion/neo-go/pkg/util"
 	"github.com/CityOfZion/neo-go/pkg/vm"
 	"github.com/CityOfZion/neo-go/pkg/vm/compiler"
@@ -27,6 +28,7 @@ var (
 	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")
 	errNoWIF               = errors.New("no WIF parameter found, specify it with the '--wif or -w' flag")
+	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")
 	errFileExist           = errors.New("A file with given smart-contract name already exists")
 )
@@ -95,6 +97,80 @@ func NewCommands() []cli.Command {
 					},
 				},
 			},
+			{
+				Name:      "testinvokefunction",
+				Usage:     "invoke deployed contract on the blockchain (test mode)",
+				UsageText: "neo-go contract testinvokefunction -e endpoint scripthash [method] [arguments...]",
+				Description: `Executes given (as a script hash) deployed script with the given method and
+   arguments. If no method is given "" is passed to the script, if no arguments
+   are given, an empty array is passed. All of the given arguments are
+   encapsulated into array before invoking the script. The script thus should
+   follow the regular convention of smart contract arguments (method string and
+   an array of other arguments).
+
+   Arguments always do have regular Neo smart contract parameter types, either
+   specified explicitly or being inferred from the value. To specify the type
+   manually use "type:value" syntax where the type is one of the following:
+   'signature', 'bool', 'int', 'hash160', 'hash256', 'bytes', 'key' or 'string'.
+   Array types are not currently supported.
+
+   Given values are type-checked against given types with the following
+   restrictions applied:
+    * 'signature' type values should be hex-encoded and have a (decoded)
+      length of 64 bytes.
+    * 'bool' type values are 'true' and 'false'.
+    * 'int' values are decimal integers that can be successfully converted
+      from the string.
+    * 'hash160' values are Neo addresses and hex-encoded 20-bytes long (after
+      decoding) strings.
+    * 'hash256' type values should be hex-encoded and have a (decoded)
+      length of 32 bytes.
+    * 'bytes' type values are any hex-encoded things.
+    * 'key' type values are hex-encoded marshalled public keys.
+    * 'string' type values are any valid UTF-8 strings. In the value's part of
+      the string the colon looses it's special meaning as a separator between
+      type and value and is taken literally.
+
+   If no type is explicitly specified, it is inferred from the value using the
+   following logic:
+    - anything that can be interpreted as a decimal integer gets
+      an 'int' type
+    - 'true' and 'false' strings get 'bool' type
+    - valid Neo addresses and 20 bytes long hex-encoded strings get 'hash160'
+      type
+    - valid hex-encoded public keys get 'key' type
+    - 32 bytes long hex-encoded values get 'hash256' type
+    - 64 bytes long hex-encoded values get 'signature' type
+    - any other valid hex-encoded values get 'bytes' type
+    - anything else is a 'string'
+
+   Backslash character is used as an escape character and allows to use colon in
+   an implicitly typed string. For any other characters it has no special
+   meaning, to get a literal backslash in the string use the '\\' sequence.
+
+   Examples:
+    * 'int:42' is an integer with a value of 42
+    * '42' is an integer with a value of 42
+    * 'bad' is a string with a value of 'bad'
+    * 'dead' is a byte array with a value of 'dead'
+    * 'string:dead' is a string with a value of 'dead'
+    * 'AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y' is a hash160 with a value
+      of '23ba2703c53263e8d6e522dc32203339dcd8eee9'
+    * '\4\2' is an integer with a value of 42
+    * '\\4\2' is a string with a value of '\42'
+    * 'string:string' is a string with a value of 'string'
+    * 'string\:string' is a string with a value of 'string:string'
+    * '03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c' is a
+      key with a value of '03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c'
+`,
+				Action: testInvokeFunction,
+				Flags: []cli.Flag{
+					cli.StringFlag{
+						Name:  "endpoint, e",
+						Usage: "RPC endpoint address (like 'http://seed4.ngd.network:20332')",
+					},
+				},
+			},
 			{
 				Name:   "testinvokescript",
 				Usage:  "Invoke compiled AVM code on the blockchain (test mode, not creating a transaction for it)",
@@ -211,6 +287,52 @@ func contractCompile(ctx *cli.Context) error {
 	return nil
 }
 
+func testInvokeFunction(ctx *cli.Context) error {
+	endpoint := ctx.String("endpoint")
+	if len(endpoint) == 0 {
+		return cli.NewExitError(errNoEndpoint, 1)
+	}
+
+	args := ctx.Args()
+	if !args.Present() {
+		return cli.NewExitError(errNoScriptHash, 1)
+	}
+	script := args[0]
+	operation := ""
+	if len(args) > 1 {
+		operation = args[1]
+	}
+	params := make([]smartcontract.Parameter, 0)
+	if len(args) > 2 {
+		for k, s := range args[2:] {
+			param, err := smartcontract.NewParameterFromString(s)
+			if err != nil {
+				return cli.NewExitError(fmt.Errorf("failed to parse argument #%d: %v", k+2+1, err), 1)
+			}
+			params = append(params, *param)
+		}
+	}
+
+	client, err := rpc.NewClient(context.TODO(), endpoint, rpc.ClientOptions{})
+	if err != nil {
+		return cli.NewExitError(err, 1)
+	}
+
+	resp, err := client.InvokeFunction(script, operation, params)
+	if err != nil {
+		return cli.NewExitError(err, 1)
+	}
+
+	b, err := json.MarshalIndent(resp.Result, "", "  ")
+	if err != nil {
+		return cli.NewExitError(err, 1)
+	}
+
+	fmt.Println(string(b))
+
+	return nil
+}
+
 func testInvokeScript(ctx *cli.Context) error {
 	src := ctx.String("in")
 	if len(src) == 0 {
diff --git a/pkg/smartcontract/param_context.go b/pkg/smartcontract/param_context.go
index 98d32a1f9..ad509f17d 100644
--- a/pkg/smartcontract/param_context.go
+++ b/pkg/smartcontract/param_context.go
@@ -1,6 +1,14 @@
 package smartcontract
 
 import (
+	"encoding/hex"
+	"errors"
+	"strconv"
+	"strings"
+	"unicode/utf8"
+
+	"github.com/CityOfZion/neo-go/pkg/crypto"
+	"github.com/CityOfZion/neo-go/pkg/crypto/keys"
 	"github.com/CityOfZion/neo-go/pkg/io"
 	"github.com/CityOfZion/neo-go/pkg/util"
 )
@@ -89,6 +97,203 @@ func NewParameter(t ParamType) Parameter {
 	}
 }
 
+// parseParamType is a user-friendly string to ParamType converter, it's
+// case-insensitive and makes the following conversions:
+//     signature -> SignatureType
+//     bool -> BoolType
+//     int -> IntegerType
+//     hash160 -> Hash160Type
+//     hash256 -> Hash256Type
+//     bytes -> ByteArrayType
+//     key -> PublicKeyType
+//     string -> StringType
+// anything else generates an error.
+func parseParamType(typ string) (ParamType, error) {
+	switch strings.ToLower(typ) {
+	case "signature":
+		return SignatureType, nil
+	case "bool":
+		return BoolType, nil
+	case "int":
+		return IntegerType, nil
+	case "hash160":
+		return Hash160Type, nil
+	case "hash256":
+		return Hash256Type, nil
+	case "bytes":
+		return ByteArrayType, nil
+	case "key":
+		return PublicKeyType, nil
+	case "string":
+		return StringType, nil
+	default:
+		// We deliberately don't support array here.
+		return 0, errors.New("wrong or unsupported parameter type")
+	}
+}
+
+// adjustValToType is a value type-checker and converter.
+func adjustValToType(typ ParamType, val string) (interface{}, error) {
+	switch typ {
+	case SignatureType:
+		b, err := hex.DecodeString(val)
+		if err != nil {
+			return nil, err
+		}
+		if len(b) != 64 {
+			return nil, errors.New("not a signature")
+		}
+		return val, nil
+	case BoolType:
+		switch val {
+		case "true":
+			return true, nil
+		case "false":
+			return false, nil
+		default:
+			return nil, errors.New("invalid boolean value")
+		}
+	case IntegerType:
+		return strconv.Atoi(val)
+	case Hash160Type:
+		u, err := crypto.Uint160DecodeAddress(val)
+		if err == nil {
+			return hex.EncodeToString(u.Bytes()), nil
+		}
+		b, err := hex.DecodeString(val)
+		if err != nil {
+			return nil, err
+		}
+		if len(b) != 20 {
+			return nil, errors.New("not a hash160")
+		}
+		return val, nil
+	case Hash256Type:
+		b, err := hex.DecodeString(val)
+		if err != nil {
+			return nil, err
+		}
+		if len(b) != 32 {
+			return nil, errors.New("not a hash256")
+		}
+		return val, nil
+	case ByteArrayType:
+		_, err := hex.DecodeString(val)
+		if err != nil {
+			return nil, err
+		}
+		return val, nil
+	case PublicKeyType:
+		_, err := keys.NewPublicKeyFromString(val)
+		if err != nil {
+			return nil, err
+		}
+		return val, nil
+	case StringType:
+		return val, nil
+	default:
+		return nil, errors.New("unsupported parameter type")
+	}
+}
+
+// inferParamType tries to infer the value type from its contents. It returns
+// IntegerType for anything that looks like decimal integer (can be converted
+// with strconv.Atoi), BoolType for true and false values, Hash160Type for
+// addresses and hex strings encoding 20 bytes long values, PublicKeyType for
+// valid hex-encoded public keys, Hash256Type for hex-encoded 32 bytes values,
+// SignatureType for hex-encoded 64 bytes values, ByteArrayType for any other
+// valid hex-encoded values and StringType for anything else.
+func inferParamType(val string) ParamType {
+	var err error
+
+	_, err = strconv.Atoi(val)
+	if err == nil {
+		return IntegerType
+	}
+
+	if val == "true" || val == "false" {
+		return BoolType
+	}
+
+	_, err = crypto.Uint160DecodeAddress(val)
+	if err == nil {
+		return Hash160Type
+	}
+
+	_, err = keys.NewPublicKeyFromString(val)
+	if err == nil {
+		return PublicKeyType
+	}
+
+	unhexed, err := hex.DecodeString(val)
+	if err == nil {
+		switch len(unhexed) {
+		case 20:
+			return Hash160Type
+		case 32:
+			return Hash256Type
+		case 64:
+			return SignatureType
+		default:
+			return ByteArrayType
+		}
+	}
+	// Anything can be a string.
+	return StringType
+}
+
+// NewParameterFromString returns a new Parameter initialized from the given
+// string in neo-go-specific format. It is intended to be used in user-facing
+// interfaces and has some heuristics in it to simplify parameter passing. Exact
+// syntax is documented in the cli documentation.
+func NewParameterFromString(in string) (*Parameter, error) {
+	var (
+		char    rune
+		val     string
+		err     error
+		r       *strings.Reader
+		buf     strings.Builder
+		escaped bool
+		hadType bool
+		res     = &Parameter{}
+	)
+	r = strings.NewReader(in)
+	for char, _, err = r.ReadRune(); err == nil && char != utf8.RuneError; char, _, err = r.ReadRune() {
+		if char == '\\' && !escaped {
+			escaped = true
+			continue
+		}
+		if char == ':' && !escaped && !hadType {
+			typStr := buf.String()
+			res.Type, err = parseParamType(typStr)
+			if err != nil {
+				return nil, err
+			}
+			buf.Reset()
+			hadType = true
+			continue
+		}
+		escaped = false
+		// We don't care about length and it never fails.
+		_, _ = buf.WriteRune(char)
+	}
+	if char == utf8.RuneError {
+		return nil, errors.New("bad UTF-8 string")
+	}
+	// The only other error `ReadRune` returns is io.EOF, which is fine and
+	// expected, so we don't check err here.
+
+	val = buf.String()
+	if !hadType {
+		res.Type = inferParamType(val)
+	}
+	res.Value, err = adjustValToType(res.Type, val)
+	if err != nil {
+		return nil, err
+	}
+	return res, nil
+}
+
 // ContextItem represents a transaction context item.
 type ContextItem struct {
 	Script     util.Uint160
diff --git a/pkg/smartcontract/param_context_test.go b/pkg/smartcontract/param_context_test.go
new file mode 100644
index 000000000..1f95d3128
--- /dev/null
+++ b/pkg/smartcontract/param_context_test.go
@@ -0,0 +1,343 @@
+package smartcontract
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestParseParamType(t *testing.T) {
+	var inouts = []struct {
+		in  string
+		out ParamType
+		err bool
+	}{{
+		in:  "signature",
+		out: SignatureType,
+	}, {
+		in:  "Signature",
+		out: SignatureType,
+	}, {
+		in:  "SiGnAtUrE",
+		out: SignatureType,
+	}, {
+		in:  "bool",
+		out: BoolType,
+	}, {
+		in:  "int",
+		out: IntegerType,
+	}, {
+		in:  "hash160",
+		out: Hash160Type,
+	}, {
+		in:  "hash256",
+		out: Hash256Type,
+	}, {
+		in:  "bytes",
+		out: ByteArrayType,
+	}, {
+		in:  "key",
+		out: PublicKeyType,
+	}, {
+		in:  "string",
+		out: StringType,
+	}, {
+		in:  "array",
+		err: true,
+	}, {
+		in:  "qwerty",
+		err: true,
+	}}
+	for _, inout := range inouts {
+		out, err := parseParamType(inout.in)
+		if inout.err {
+			assert.NotNil(t, err, "should error on '%s' input", inout.in)
+		} else {
+			assert.Nil(t, err, "shouldn't error on '%s' input", inout.in)
+			assert.Equal(t, inout.out, out, "bad output for '%s' input", inout.in)
+		}
+	}
+}
+
+func TestInferParamType(t *testing.T) {
+	var inouts = []struct {
+		in  string
+		out ParamType
+	}{{
+		in:  "42",
+		out: IntegerType,
+	}, {
+		in:  "-42",
+		out: IntegerType,
+	}, {
+		in:  "0",
+		out: IntegerType,
+	}, {
+		in:  "2e10",
+		out: ByteArrayType,
+	}, {
+		in:  "true",
+		out: BoolType,
+	}, {
+		in:  "false",
+		out: BoolType,
+	}, {
+		in:  "truee",
+		out: StringType,
+	}, {
+		in:  "AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y",
+		out: Hash160Type,
+	}, {
+		in:  "ZK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y",
+		out: StringType,
+	}, {
+		in:  "50befd26fdf6e4d957c11e078b24ebce6291456f",
+		out: Hash160Type,
+	}, {
+		in:  "03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c",
+		out: PublicKeyType,
+	}, {
+		in:  "30b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c",
+		out: ByteArrayType,
+	}, {
+		in:  "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7",
+		out: Hash256Type,
+	}, {
+		in:  "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7da",
+		out: ByteArrayType,
+	}, {
+		in:  "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b",
+		out: SignatureType,
+	}, {
+		in:  "qwerty",
+		out: StringType,
+	}, {
+		in:  "ab",
+		out: ByteArrayType,
+	}, {
+		in:  "az",
+		out: StringType,
+	}, {
+		in:  "bad",
+		out: StringType,
+	}, {
+		in:  "фыва",
+		out: StringType,
+	}, {
+		in:  "dead",
+		out: ByteArrayType,
+	}}
+	for _, inout := range inouts {
+		out := inferParamType(inout.in)
+		assert.Equal(t, inout.out, out, "bad output for '%s' input", inout.in)
+	}
+}
+
+func TestAdjustValToType(t *testing.T) {
+	var inouts = []struct {
+		typ ParamType
+		val string
+		out interface{}
+		err bool
+	}{{
+		typ: SignatureType,
+		val: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b",
+		out: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b",
+	}, {
+		typ: SignatureType,
+		val: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c",
+		err: true,
+	}, {
+		typ: SignatureType,
+		val: "qwerty",
+		err: true,
+	}, {
+		typ: BoolType,
+		val: "false",
+		out: false,
+	}, {
+		typ: BoolType,
+		val: "true",
+		out: true,
+	}, {
+		typ: BoolType,
+		val: "qwerty",
+		err: true,
+	}, {
+		typ: BoolType,
+		val: "42",
+		err: true,
+	}, {
+		typ: BoolType,
+		val: "0",
+		err: true,
+	}, {
+		typ: IntegerType,
+		val: "0",
+		out: 0,
+	}, {
+		typ: IntegerType,
+		val: "42",
+		out: 42,
+	}, {
+		typ: IntegerType,
+		val: "-42",
+		out: -42,
+	}, {
+		typ: IntegerType,
+		val: "q",
+		err: true,
+	}, {
+		typ: Hash160Type,
+		val: "AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y",
+		out: "23ba2703c53263e8d6e522dc32203339dcd8eee9",
+	}, {
+		typ: Hash160Type,
+		val: "50befd26fdf6e4d957c11e078b24ebce6291456f",
+		out: "50befd26fdf6e4d957c11e078b24ebce6291456f",
+	}, {
+		typ: Hash160Type,
+		val: "befd26fdf6e4d957c11e078b24ebce6291456f",
+		err: true,
+	}, {
+		typ: Hash160Type,
+		val: "q",
+		err: true,
+	}, {
+		typ: Hash256Type,
+		val: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7",
+		out: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7",
+	}, {
+		typ: Hash256Type,
+		val: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282d",
+		err: true,
+	}, {
+		typ: Hash256Type,
+		val: "q",
+		err: true,
+	}, {
+		typ: ByteArrayType,
+		val: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282d",
+		out: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282d",
+	}, {
+		typ: ByteArrayType,
+		val: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7",
+		out: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7",
+	}, {
+		typ: ByteArrayType,
+		val: "50befd26fdf6e4d957c11e078b24ebce6291456f",
+		out: "50befd26fdf6e4d957c11e078b24ebce6291456f",
+	}, {
+		typ: ByteArrayType,
+		val: "AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y",
+		err: true,
+	}, {
+		typ: ByteArrayType,
+		val: "q",
+		err: true,
+	}, {
+		typ: ByteArrayType,
+		val: "ab",
+		out: "ab",
+	}, {
+		typ: PublicKeyType,
+		val: "03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c",
+		out: "03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c",
+	}, {
+		typ: PublicKeyType,
+		val: "01b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c",
+		err: true,
+	}, {
+		typ: PublicKeyType,
+		val: "q",
+		err: true,
+	}, {
+		typ: StringType,
+		val: "q",
+		out: "q",
+	}, {
+		typ: StringType,
+		val: "dead",
+		out: "dead",
+	}, {
+		typ: StringType,
+		val: "йцукен",
+		out: "йцукен",
+	}, {
+		typ: ArrayType,
+		val: "",
+		err: true,
+	}}
+
+	for _, inout := range inouts {
+		out, err := adjustValToType(inout.typ, inout.val)
+		if inout.err {
+			assert.NotNil(t, err, "should error on '%s/%s' input", inout.typ, inout.val)
+		} else {
+			assert.Nil(t, err, "shouldn't error on '%s/%s' input", inout.typ, inout.val)
+			assert.Equal(t, inout.out, out, "bad output for '%s/%s' input", inout.typ, inout.val)
+		}
+	}
+}
+
+func TestNewParameterFromString(t *testing.T) {
+	var inouts = []struct {
+		in  string
+		out Parameter
+		err bool
+	}{{
+		in:  "qwerty",
+		out: Parameter{StringType, "qwerty"},
+	}, {
+		in:  "42",
+		out: Parameter{IntegerType, 42},
+	}, {
+		in:  "Hello, 世界",
+		out: Parameter{StringType, "Hello, 世界"},
+	}, {
+		in:  `\4\2`,
+		out: Parameter{IntegerType, 42},
+	}, {
+		in:  `\\4\2`,
+		out: Parameter{StringType, `\42`},
+	}, {
+		in:  `\\\4\2`,
+		out: Parameter{StringType, `\42`},
+	}, {
+		in:  "int:42",
+		out: Parameter{IntegerType, 42},
+	}, {
+		in:  "true",
+		out: Parameter{BoolType, true},
+	}, {
+		in:  "string:true",
+		out: Parameter{StringType, "true"},
+	}, {
+		in:  "\xfe\xff",
+		err: true,
+	}, {
+		in:  `string\:true`,
+		out: Parameter{StringType, "string:true"},
+	}, {
+		in:  "string:true:true",
+		out: Parameter{StringType, "true:true"},
+	}, {
+		in:  `string\\:true`,
+		err: true,
+	}, {
+		in:  `qwerty:asdf`,
+		err: true,
+	}, {
+		in:  `bool:asdf`,
+		err: true,
+	}}
+	for _, inout := range inouts {
+		out, err := NewParameterFromString(inout.in)
+		if inout.err {
+			assert.NotNil(t, err, "should error on '%s' input", inout.in)
+		} else {
+			assert.Nil(t, err, "shouldn't error on '%s' input", inout.in)
+			assert.Equal(t, inout.out, *out, "bad output for '%s' input", inout.in)
+		}
+	}
+}