Skip to content

Commit 8da0e6c

Browse files
authored
feat: add ContractCreateTransaction (#198)
* feat: add ContractSelector class Signed-off-by: dosi <[email protected]> * feat: add ContractFunctionParamters Signed-off-by: dosi <[email protected]> * feat: add ContractId class Signed-off-by: dosi <[email protected]> * test: add unit tests for contractId Signed-off-by: dosi <[email protected]> * feat: export contract_id in transaction_receipt Signed-off-by: dosi <[email protected]> * feat: add ContractCreateTransaction class Signed-off-by: dosi <[email protected]> * test: add unit tests for ContractCreateTransaction Signed-off-by: dosi <[email protected]> * test: add integration tests forContractCreateTransaction Signed-off-by: dosi <[email protected]> * docs: add ContractCreateTransaction in examples README Signed-off-by: dosi <[email protected]> * docs: add contract create example Signed-off-by: dosi <[email protected]> * feat: add contract bytecode files (SimpleContract for basic testing, StatefulContract for state management) Signed-off-by: dosi <[email protected]> * feat: add contract bytecode loader utility and examples module initialization Signed-off-by: dosi <[email protected]> * test: update ContractCreate integration tests to use contract utils and add constructor test Signed-off-by: dosi <[email protected]> * chore: fix linting issues in contract utilities Signed-off-by: dosi <[email protected]> * docs: update contract create example Signed-off-by: dosi <[email protected]> * docs: add contract create with constructor example Signed-off-by: dosi <[email protected]> * chore: address PR feedback for contract create Signed-off-by: dosi <[email protected]> * refactor: migrate ContractFunctionParameters to use eth-abi for parameter encoding Signed-off-by: dosi <[email protected]> * feat: add type stub file for ContractFunctionParameters with eth-abi support Signed-off-by: dosi <[email protected]> * docs: update examples Signed-off-by: dosi <[email protected]> * docs: add contract creation example with direct bytecode usage Signed-off-by: dosi <[email protected]> * chore: address PR feedback Signed-off-by: dosi <[email protected]> * fix: remove unnecessary added add_function() method from ContractFunctionParameters Signed-off-by: dosi <[email protected]> * feat: add ConstructorTestContract smart contract for testing ContractFunctionParameters Signed-off-by: dosi <[email protected]> * test: add integration tests for ContracFunctionParameters Signed-off-by: dosi <[email protected]> * chore: add ContractCreateTransaction and ContractFunctionParameters to __init__.py Signed-off-by: dosi <[email protected]> * feat: add eth-abi dependency for contract parameter encoding Signed-off-by: dosi <[email protected]> * fix: update ConstructorTestContract bytecode to match compiled version Signed-off-by: dosi <[email protected]> * docs: add parameter order comments in contract test Signed-off-by: dosi <[email protected]> * docs: add more documentaton in contract utils Signed-off-by: dosi <[email protected]> * chore: address PR feedback Signed-off-by: dosi <[email protected]> * chore: address incorrect error raising Signed-off-by: dosi <[email protected]> --------- Signed-off-by: dosi <[email protected]>
1 parent a599435 commit 8da0e6c

24 files changed

+3997
-1
lines changed

examples/README.md

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ You can choose either syntax or even mix both styles in your projects.
5252
- [Querying File Contents](#querying-file-contents)
5353
- [Updating a File](#updating-a-file)
5454
- [Deleting a File](#deleting-a-file)
55+
- [Contract Transactions](#contract-transactions)
56+
- [Creating a Contract](#creating-a-contract)
5557
- [Miscellaneous Queries](#miscellaneous-queries)
5658
- [Querying Transaction Record](#querying-transaction-record)
5759

@@ -1043,6 +1045,106 @@ transaction.execute(client)
10431045
10441046
```
10451047

1048+
```
1049+
1050+
## Contract Transactions
1051+
1052+
### Creating a Contract
1053+
1054+
#### Pythonic Syntax:
1055+
```
1056+
# First, create a file with the contract bytecode
1057+
transaction = FileCreateTransaction(
1058+
keys=[operator_key.public_key()],
1059+
contents=contract_bytecode,
1060+
file_memo="Contract bytecode file"
1061+
).freeze_with(client)
1062+
1063+
transaction.sign(operator_key)
1064+
file_receipt = transaction.execute(client)
1065+
1066+
file_id = file_receipt.file_id
1067+
1068+
# Create constructor parameters if needed
1069+
constructor_params = ContractFunctionParameters().add_string("Hello, World!")
1070+
1071+
# Create the contract using bytecode from file
1072+
transaction = ContractCreateTransaction(
1073+
contract_params=ContractCreateParams(
1074+
bytecode_file_id=file_id,
1075+
gas=200000,
1076+
admin_key=admin_key,
1077+
initial_balance=100000000, # 1 HBAR in tinybars
1078+
parameters=constructor_params.to_bytes(),
1079+
contract_memo="My first smart contract"
1080+
)
1081+
).freeze_with(client)
1082+
1083+
transaction.sign(operator_key)
1084+
transaction.sign(admin_key)
1085+
transaction.execute(client)
1086+
```
1087+
1088+
#### Method Chaining:
1089+
```
1090+
# First, create a file with the contract bytecode
1091+
file_receipt = (
1092+
FileCreateTransaction()
1093+
.set_keys(operator_key.public_key())
1094+
.set_contents(contract_bytecode)
1095+
.set_file_memo("Contract bytecode file")
1096+
.freeze_with(client)
1097+
.sign(operator_key)
1098+
.execute(client)
1099+
)
1100+
1101+
file_id = file_receipt.file_id
1102+
1103+
# Create constructor parameters if needed
1104+
constructor_params = ContractFunctionParameters().add_string("Hello, World!")
1105+
1106+
# Create the contract using bytecode from file
1107+
transaction = (
1108+
ContractCreateTransaction()
1109+
.set_bytecode_file_id(file_id)
1110+
.set_gas(200000)
1111+
.set_admin_key(admin_key)
1112+
.set_initial_balance(100000000) # 1 HBAR in tinybars
1113+
.set_constructor_parameters(constructor_params)
1114+
.set_contract_memo("My first smart contract")
1115+
.freeze_with(client)
1116+
)
1117+
1118+
transaction.sign(operator_key)
1119+
transaction.sign(admin_key)
1120+
transaction.execute(client)
1121+
```
1122+
1123+
#### Creating a Contract with Direct Bytecode:
1124+
```
1125+
##### Convert hex bytecode to bytes
1126+
bytecode = bytes.fromhex(contract_bytecode_hex)
1127+
1128+
# Create constructor parameters if needed
1129+
constructor_params = ContractFunctionParameters().add_string("Hello, World!")
1130+
1131+
# Create the contract using bytecode directly
1132+
transaction = (
1133+
ContractCreateTransaction()
1134+
.set_bytecode(bytecode)
1135+
.set_gas(200000)
1136+
.set_admin_key(admin_key)
1137+
.set_initial_balance(100000000) # 1 HBAR in tinybars
1138+
.set_constructor_parameters(constructor_params)
1139+
.set_contract_memo("My first smart contract")
1140+
.freeze_with(client)
1141+
)
1142+
1143+
transaction.sign(operator_key)
1144+
transaction.sign(admin_key)
1145+
transaction.execute(client)
1146+
```
1147+
10461148
## Miscellaneous Queries
10471149
10481150
### Querying Transaction Record

examples/contract_create.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
"""
2+
Example demonstrating contract creation on the network.
3+
4+
This module shows how to create a smart contract by:
5+
1. Setting up a client with operator credentials
6+
2. Creating a file containing contract bytecode
7+
3. Creating a contract using the file
8+
9+
Usage:
10+
# Due to the way the script is structured, it must be run as a module
11+
# from the project root directory
12+
13+
# Run from the project root directory
14+
python -m examples.contract_create
15+
16+
"""
17+
18+
import os
19+
import sys
20+
21+
from dotenv import load_dotenv
22+
23+
from hiero_sdk_python import AccountId, Client, Network, PrivateKey
24+
from hiero_sdk_python.contract.contract_create_transaction import (
25+
ContractCreateTransaction,
26+
)
27+
from hiero_sdk_python.file.file_create_transaction import FileCreateTransaction
28+
from hiero_sdk_python.response_code import ResponseCode
29+
30+
# Import the bytecode for a basic smart contract (SimpleContract.sol) that can be deployed
31+
# The contract bytecode is pre-compiled from Solidity source code
32+
from .contracts import SIMPLE_CONTRACT_BYTECODE
33+
34+
load_dotenv()
35+
36+
37+
def setup_client():
38+
"""Initialize and set up the client with operator account"""
39+
network = Network(network="testnet")
40+
client = Client(network)
41+
42+
operator_id = AccountId.from_string(os.getenv("OPERATOR_ID"))
43+
operator_key = PrivateKey.from_string(os.getenv("OPERATOR_KEY"))
44+
client.set_operator(operator_id, operator_key)
45+
46+
return client
47+
48+
49+
def create_contract_file(client):
50+
"""Create a file containing the contract bytecode"""
51+
file_receipt = (
52+
FileCreateTransaction()
53+
.set_keys(client.operator_private_key.public_key())
54+
.set_contents(SIMPLE_CONTRACT_BYTECODE)
55+
.set_file_memo("Contract bytecode file")
56+
.execute(client)
57+
)
58+
59+
# Check if file creation was successful
60+
if file_receipt.status != ResponseCode.SUCCESS:
61+
print(
62+
f"File creation failed with status: {ResponseCode(file_receipt.status).name}"
63+
)
64+
sys.exit(1)
65+
66+
return file_receipt.file_id
67+
68+
69+
def contract_create():
70+
"""
71+
Demonstrates creating a contract on the network by:
72+
1. Setting up client with operator account
73+
2. Creating a file containing contract bytecode
74+
3. Creating a contract using the file
75+
"""
76+
client = setup_client()
77+
78+
file_id = create_contract_file(client)
79+
80+
# Create contract using the file
81+
receipt = (
82+
ContractCreateTransaction()
83+
.set_bytecode_file_id(file_id)
84+
.set_gas(2000000) # 2M gas
85+
.set_contract_memo("My first smart contract")
86+
.execute(client)
87+
)
88+
89+
# Check if contract creation was successful
90+
if receipt.status != ResponseCode.SUCCESS:
91+
print(
92+
f"Contract creation failed with status: {ResponseCode(receipt.status).name}"
93+
)
94+
sys.exit(1)
95+
96+
contract_id = receipt.contract_id
97+
print(f"Contract created successfully with ID: {contract_id}")
98+
99+
100+
if __name__ == "__main__":
101+
contract_create()
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
"""
2+
Example demonstrating contract creation with constructor parameters on the network.
3+
4+
This module shows how to create a stateful smart contract by:
5+
1. Setting up a client with operator credentials
6+
2. Creating a file containing contract bytecode
7+
3. Creating a contract using the file and constructor parameters
8+
9+
Usage:
10+
# Due to the way the script is structured, it must be run as a module
11+
# from the project root directory
12+
13+
# Run from the project root directory
14+
python -m examples.contract_create_constructor
15+
16+
"""
17+
18+
import os
19+
import sys
20+
21+
from dotenv import load_dotenv
22+
23+
from hiero_sdk_python import AccountId, Client, Network, PrivateKey
24+
from hiero_sdk_python.contract.contract_create_transaction import (
25+
ContractCreateTransaction,
26+
)
27+
from hiero_sdk_python.contract.contract_function_parameters import (
28+
ContractFunctionParameters,
29+
)
30+
from hiero_sdk_python.file.file_create_transaction import FileCreateTransaction
31+
from hiero_sdk_python.response_code import ResponseCode
32+
33+
# Import the bytecode for a stateful smart contract (StatefulContract.sol) that can be deployed
34+
# The contract bytecode is pre-compiled from Solidity source code
35+
from .contracts import STATEFUL_CONTRACT_BYTECODE
36+
37+
load_dotenv()
38+
39+
40+
def setup_client():
41+
"""Initialize and set up the client with operator account"""
42+
network = Network(network="testnet")
43+
client = Client(network)
44+
45+
operator_id = AccountId.from_string(os.getenv("OPERATOR_ID"))
46+
operator_key = PrivateKey.from_string(os.getenv("OPERATOR_KEY"))
47+
client.set_operator(operator_id, operator_key)
48+
49+
return client
50+
51+
52+
def create_contract_file(client):
53+
"""Create a file containing the stateful contract bytecode"""
54+
file_receipt = (
55+
FileCreateTransaction()
56+
.set_keys(client.operator_private_key.public_key())
57+
.set_contents(STATEFUL_CONTRACT_BYTECODE)
58+
.set_file_memo("Stateful contract bytecode file")
59+
.execute(client)
60+
)
61+
62+
# Check if file creation was successful
63+
if file_receipt.status != ResponseCode.SUCCESS:
64+
print(
65+
f"File creation failed with status: {ResponseCode(file_receipt.status).name}"
66+
)
67+
sys.exit(1)
68+
69+
return file_receipt.file_id
70+
71+
72+
def contract_create_constructor():
73+
"""
74+
Demonstrates creating a stateful contract with constructor parameters by:
75+
1. Setting up client with operator account
76+
2. Creating a file containing stateful contract bytecode
77+
3. Creating a contract using the file with constructor parameters
78+
"""
79+
client = setup_client()
80+
81+
file_id = create_contract_file(client)
82+
83+
# Prepare constructor parameters for the stateful contract
84+
# The contract's constructor expects a bytes32 parameter that will be stored
85+
# We need to:
86+
# 1. Convert our string message to UTF-8 bytes (encode)
87+
# 2. Pass those bytes to add_bytes32() to properly format for the contract
88+
# NOTE: If message exceeds 32 bytes, it will raise an error.
89+
# If message is less than 32 bytes, it will be padded with zeros.
90+
initial_message = "Initial message from constructor".encode("utf-8")
91+
92+
# Create ContractFunctionParameters object and add the bytes32 parameter
93+
# This will be passed to setConstructorParameters() when creating the contract
94+
constructor_params = ContractFunctionParameters().add_bytes32(initial_message)
95+
96+
# Create contract using the file with constructor parameters
97+
receipt = (
98+
ContractCreateTransaction()
99+
.set_admin_key(client.operator_private_key.public_key())
100+
.set_gas(2000000) # 2M gas
101+
.set_bytecode_file_id(file_id)
102+
.set_constructor_parameters(constructor_params)
103+
.set_contract_memo("Stateful smart contract with constructor")
104+
.execute(client)
105+
)
106+
107+
# Check if contract creation was successful
108+
if receipt.status != ResponseCode.SUCCESS:
109+
print(
110+
f"Contract creation failed with status: {ResponseCode(receipt.status).name}"
111+
)
112+
sys.exit(1)
113+
114+
contract_id = receipt.contract_id
115+
print(f"Stateful contract created successfully with ID: {contract_id}")
116+
print(f"Initial message set in constructor: '{initial_message.decode('utf-8')}'")
117+
118+
119+
if __name__ == "__main__":
120+
contract_create_constructor()

0 commit comments

Comments
 (0)