@@ -112,16 +112,18 @@ Arguments are supplied in one of two ways: through script evaluation or via hex
112
112
of the value serialization format. The method for supplying arguments is chosen by
113
113
prefacing each argument with a flag:
114
114
115
- -e indicates the argument should be _evaluated_
116
- -x indicates the argument that a serialized Clarity value is being passed (hex-serialized)
115
+ -e indicates the argument should be _evaluated_
116
+ -x indicates the argument that a serialized Clarity value is being passed (hex-serialized)
117
+ --hex-file <file_path> same as `-x`, but reads the serialized Clarity value from a file
117
118
118
119
e.g.,
119
120
120
121
blockstack-cli contract-call $secret_key 10 0 SPJT598WY1RJN792HRKRHRQYFB7RJ5ZCG6J6GEZ4 foo-contract \\
121
122
transfer-fookens -e \\ 'SPJT598WY1RJN792HRKRHRQYFB7RJ5ZCG6J6GEZ4 \\
122
123
-e \" (+ 1 2)\" \\
123
124
-x 0000000000000000000000000000000001 \\
124
- -x 050011deadbeef11ababffff11deadbeef11ababffff
125
+ -x 050011deadbeef11ababffff11deadbeef11ababffff \\
126
+ --hex-file /path/to/value.hex
125
127
" ;
126
128
127
129
const TOKEN_TRANSFER_USAGE : & str = "blockstack-cli (options) token-transfer [origin-secret-key-hex] [fee-rate] [nonce] [recipient-address] [amount] [memo] [args...]
@@ -606,7 +608,7 @@ fn handle_contract_call(
606
608
607
609
if val_args. len ( ) % 2 != 0 {
608
610
return Err (
609
- "contract-call arguments must be supplied as a list of `-e ...` or `-x 0000...` pairs"
611
+ "contract-call arguments must be supplied as a list of `-e ...` or `-x 0000...` or `--hex-file <file_path>` pairs"
610
612
. into ( ) ,
611
613
) ;
612
614
}
@@ -624,8 +626,16 @@ fn handle_contract_call(
624
626
vm_execute ( input, clarity_version) ?
625
627
. ok_or ( "Supplied argument did not evaluate to a Value" ) ?
626
628
} ,
629
+ "--hex-file" => {
630
+ let content = fs:: read_to_string ( input)
631
+ . map_err ( |e| {
632
+ let err_msg = format ! ( "Cannot read file: {input}. Reason: {e}" ) ;
633
+ CliError :: Message ( err_msg)
634
+ } ) ?;
635
+ Value :: try_deserialize_hex_untyped ( & content) ?
636
+ }
627
637
_ => {
628
- return Err ( "contract-call arguments must be supplied as a list of `-e ...` or `-x 0000...` pairs" . into ( ) )
638
+ return Err ( "contract-call arguments must be supplied as a list of `-e ...` or `-x 0000...` or `--hex-file <file_path>` pairs" . into ( ) )
629
639
}
630
640
} ;
631
641
@@ -1053,6 +1063,7 @@ mod test {
1053
1063
1054
1064
use blockstack_lib:: chainstate:: stacks:: TransactionPostCondition ;
1055
1065
use stacks_common:: util:: cargo_workspace;
1066
+ use tempfile:: NamedTempFile ;
1056
1067
1057
1068
use super :: * ;
1058
1069
@@ -1574,6 +1585,106 @@ mod test {
1574
1585
) ;
1575
1586
}
1576
1587
1588
+ #[ test]
1589
+ fn test_contract_call_with_serialized_arg_from_file_ok ( ) {
1590
+ let mut file = NamedTempFile :: new ( ) . expect ( "Cannot create tempfile!" ) ;
1591
+ write ! ( file, "0000000000000000000000000000000001" ) . expect ( "Cannot Write to temp file" ) ;
1592
+
1593
+ let file_path = file. path ( ) . to_str ( ) . unwrap ( ) ;
1594
+ let cc_args = [
1595
+ "contract-call" ,
1596
+ "043ff5004e3d695060fa48ac94c96049b8c14ef441c50a184a6a3875d2a000f3" ,
1597
+ "1" ,
1598
+ "0" ,
1599
+ "SPJT598WY1RJN792HRKRHRQYFB7RJ5ZCG6J6GEZ4" ,
1600
+ "foo-contract" ,
1601
+ "transfer-fookens" ,
1602
+ "--hex-file" ,
1603
+ file_path,
1604
+ ] ;
1605
+
1606
+ let result = main_handler ( to_string_vec ( & cc_args) ) ;
1607
+ assert ! ( result. is_ok( ) , "Result should be ok!" ) ;
1608
+
1609
+ let expected_tx = "0000000001040021a3c334fc0ee50359353799e8b2605ac6be1fe400000000000000000000000000000001010011db0868db0cd44c463b3a8a8b3b428ddaad15661e7b7d8c92c814c142c526e30abffe74e1e098f517037a1ee74969f4db27630407f4c958cb0d6e1d7485fe06030200000000021625a2a51cf0712a9d228e2788e2fe7acf8917ec810c666f6f2d636f6e7472616374107472616e736665722d666f6f6b656e73000000010000000000000000000000000000000001" ;
1610
+ assert_eq ! ( expected_tx, result. unwrap( ) ) ;
1611
+ }
1612
+
1613
+ #[ test]
1614
+ fn test_contract_call_with_serialized_arg_from_file_fails_due_to_file ( ) {
1615
+ let file_path = "/tmp/this-file-not-exists" ;
1616
+ let cc_args = [
1617
+ "contract-call" ,
1618
+ "043ff5004e3d695060fa48ac94c96049b8c14ef441c50a184a6a3875d2a000f3" ,
1619
+ "1" ,
1620
+ "0" ,
1621
+ "SPJT598WY1RJN792HRKRHRQYFB7RJ5ZCG6J6GEZ4" ,
1622
+ "foo-contract" ,
1623
+ "transfer-fookens" ,
1624
+ "--hex-file" ,
1625
+ file_path,
1626
+ ] ;
1627
+
1628
+ let result = main_handler ( to_string_vec ( & cc_args) ) ;
1629
+ assert ! ( result. is_err( ) , "Result should be err!" ) ;
1630
+
1631
+ let expected_msg = format ! ( "Cannot read file: {}. Reason: " , file_path) ;
1632
+ assert ! ( result. unwrap_err( ) . to_string( ) . starts_with( & expected_msg) ) ;
1633
+ }
1634
+
1635
+ #[ test]
1636
+ fn test_contract_call_with_serialized_arg_from_file_fails_due_to_bad_hex ( ) {
1637
+ let mut file = NamedTempFile :: new ( ) . expect ( "Cannot create tempfile!" ) ;
1638
+ // Bad hex string but (good except for the \n)
1639
+ write ! ( file, "0000000000000000000000000000000001\n " ) . expect ( "Cannot Write to temp file" ) ;
1640
+ let file_path = file. path ( ) . to_str ( ) . unwrap ( ) ;
1641
+
1642
+ let cc_args = [
1643
+ "contract-call" ,
1644
+ "043ff5004e3d695060fa48ac94c96049b8c14ef441c50a184a6a3875d2a000f3" ,
1645
+ "1" ,
1646
+ "0" ,
1647
+ "SPJT598WY1RJN792HRKRHRQYFB7RJ5ZCG6J6GEZ4" ,
1648
+ "foo-contract" ,
1649
+ "transfer-fookens" ,
1650
+ "--hex-file" ,
1651
+ & file_path,
1652
+ ] ;
1653
+
1654
+ let result = main_handler ( to_string_vec ( & cc_args) ) ;
1655
+ assert ! ( result. is_err( ) , "Result should be err!" ) ;
1656
+
1657
+ let expected_msg = "Failed to deserialize: Deserialization error: Bad hex string" ;
1658
+ assert_eq ! ( expected_msg, result. unwrap_err( ) . to_string( ) ) ;
1659
+ }
1660
+
1661
+ #[ test]
1662
+ fn test_contract_call_with_serialized_arg_from_file_fails_due_to_short_buffer ( ) {
1663
+ let mut file = NamedTempFile :: new ( ) . expect ( "Cannot create tempfile!" ) ;
1664
+ // hex buffer is short
1665
+ write ! ( file, "0101" ) . expect ( "Cannot Write to temp file" ) ;
1666
+ let file_path = file. path ( ) . to_str ( ) . unwrap ( ) ;
1667
+
1668
+ let cc_args = [
1669
+ "contract-call" ,
1670
+ "043ff5004e3d695060fa48ac94c96049b8c14ef441c50a184a6a3875d2a000f3" ,
1671
+ "1" ,
1672
+ "0" ,
1673
+ "SPJT598WY1RJN792HRKRHRQYFB7RJ5ZCG6J6GEZ4" ,
1674
+ "foo-contract" ,
1675
+ "transfer-fookens" ,
1676
+ "--hex-file" ,
1677
+ & file_path,
1678
+ ] ;
1679
+
1680
+ let result = main_handler ( to_string_vec ( & cc_args) ) ;
1681
+ assert ! ( result. is_err( ) , "Result should be err!" ) ;
1682
+
1683
+ let expected_msg =
1684
+ "Failed to deserialize: Serialization error caused by IO: failed to fill whole buffer" ;
1685
+ assert_eq ! ( expected_msg, result. unwrap_err( ) . to_string( ) ) ;
1686
+ }
1687
+
1577
1688
#[ test]
1578
1689
fn simple_addresses ( ) {
1579
1690
let addr_args = [
0 commit comments