ContractUpdateTransaction - Test specification
Description:
This test specification for ContractUpdateTransaction is to be one of many for testing the functionality of the Hedera SDKs. The SDK under test will use the language specific JSON-RPC server return responses back to the test driver.
Design:
Each test within the test specification is linked to one of the properties within ContractUpdateTransaction. Each property is tested with a mix of boundaries. The inputs for each test are a range of valid, minimum, maximum, negative and invalid values for the method. The expected response of a passed test can be a correct error response code or seen as the result of node queries. A successful transaction (the transaction reached consensus and was applied to state) can be determined by getting a TransactionReceipt
or TransactionRecord
, or can be determined by using queries such as ContractInfoQuery
and investigating for the required changes (updates, etc.). The mirror node can also be used to determine if a transaction was successful via its rest API. Error codes are obtained from the response code proto files.
Transaction properties:
https://docs.hedera.com/hedera/sdks-and-apis/sdks/smart-contracts/update-a-smart-contract
ContractUpdate protobufs:
Response codes:
https://github.com/hashgraph/hedera-protobufs/blob/main/services/response_code.proto
Mirror Node APIs:
https://docs.hedera.com/hedera/sdks-and-apis/rest-api
JSON-RPC API Endpoint Documentation
Method Name
updateContract
Input Parameters
Parameter Name | Type | Required/Optional | Description/Notes |
---|---|---|---|
contractId | string | optional | The ID of the contract to update. |
adminKey | string | optional | The new admin key for the contract. DER-encoded hex string representation for private or public keys. KeyLists and ThresholdKeys are the hex of the serialized protobuf bytes. |
autoRenewPeriod | string | optional | The new auto-renew period for the contract in seconds. |
expirationTime | string | optional | The new expiration time for the contract in epoch seconds. |
memo | string | optional | The new memo for the contract (UTF-8 encoding max 100 bytes). |
autoRenewAccountId | string | optional | The ID of the account to pay for auto-renewal fees. |
maxAutomaticTokenAssociations | int32 | optional | The maximum number of tokens that can be auto-associated with this contract. |
stakedAccountId | string | optional | The ID of the account to which the contract is staked. |
stakedNodeId | string | optional | The ID of the node to which the contract is staked. |
declineStakingReward | bool | optional | Whether the contract declines staking rewards. |
commonTransactionParams | json object | optional | Standard fields: payer, signers, maxFee, etc. |
Output Parameters
Parameter Name | Type | Description/Notes |
---|---|---|
status | string | The status of the submitted ContractUpdateTransaction (from a TransactionReceipt ). |
Additional Notes
The tests contained in this specification will assume that a valid contract was already successfully created. Assume that the contract was created with default values, unless specified otherwise. Any <CREATED_CONTRACT_ID>
tag will be the contract ID of this created contract. Any <ADMIN_KEY_OF_CREATED_CONTRACT>
is the DER-encoded hex string of the admin key of the contract.
Property Tests
Contract ID:
- The ID of the contract to update.
Test no | Name | Input | Expected response | Implemented (Y/N) |
---|---|---|---|---|
1 | Updates a contract with valid contractID | contractId=<CREATED_CONTRACT_ID>, commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update succeeds. | Y |
2 | Updates a contract with no updates without signing with the contract's admin key | contractId=<CREATED_CONTRACT_ID> | The contract update succeeds. | Y |
3 | Updates a contract with no contract ID | The contract update fails with a INVALID_CONTRACT_ID response code from the network. | Y | |
4 | Updates a contract with an invalid contract ID | contractId="0.0.99999999" | The contract update fails with an INVALID_CONTRACT_ID response code from the network. | Y |
5 | Updates a contract with deleted contract ID | contractId=<DELETED_CONTRACT_ID> | The contract update fails with a CONTRACT_DELETED response code from the network. | Y |
Admin Key:
- The new admin key for the contract.
Test no | Name | Input | Expected response | Implemented (Y/N) |
---|---|---|---|---|
1 | Updates the admin key of a contract to a new valid ED25519 public key | contractId=<CREATED_CONTRACT_ID>, adminKey=<VALID_ED25519_PUBLIC_KEY>, commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>, <CORRESPONDING_VALID_ED25519_PRIVATE_KEY>] | The contract update succeeds and the contract has the new ED25519 key. | Y |
2 | Updates the admin key of a contract to a new valid ECDSAsecp256k1 public key | contractId=<CREATED_CONTRACT_ID>, adminKey=<VALID_ECDSA_SECP256K1_PUBLIC_KEY>, commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>, <CORRESPONDING_VALID_ECDSA_SECP256K1_PRIVATE_KEY>] | The contract update succeeds and the contract has the new ECDSAsecp256k1 key. | Y |
3 | Updates the admin key of a contract to a new valid ED25519 private key | contractId=<CREATED_CONTRACT_ID>, adminKey=<VALID_ED25519_PRIVATE_KEY>, commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>, <VALID_ED25519_PRIVATE_KEY>] | The contract update succeeds and the contract has the new ED25519 key. | Y |
4 | Updates the admin key of a contract to a new valid ECDSAsecp256k1 private key | contractId=<CREATED_CONTRACT_ID>, adminKey=<VALID_ECDSA_SECP256K1_PRIVATE_KEY>, commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>, <VALID_ECDSA_SECP256K1_PRIVATE_KEY>] | The contract update succeeds and the contract has the new ECDSAsecp256k1 key. | Y |
5 | Updates the admin key of a contract to a new valid KeyList of ED25519 and ECDSAsecp256k1 private and public keys | contractId=<CREATED_CONTRACT_ID>, adminKey=<VALID_KEYLIST>, commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>, <VALID_KEYLIST>] | The contract update succeeds and the contract has the new KeyList. | Y |
6 | Updates the admin key of a contract to a new valid ThresholdKey of ED25519 and ECDSAsecp256k1 private and public keys | contractId=<CREATED_CONTRACT_ID>, adminKey=<VALID_THRESHOLD_KEY>, commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>, <VALID_THRESHOLD_KEY>] | The contract update succeeds and the contract has the new ThresholdKey. | Y |
7 | Updates the admin key of a contract to a new valid key without signing with the new key | contractId=<CREATED_CONTRACT_ID>, adminKey=<VALID_KEY>, commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update fails with an INVALID_SIGNATURE response code from the network. | Y |
AutoRenewPeriod:
- The amount of time to attempt to extend the contract's lifetime automatically.
Test no | Name | Input | Expected response | Implemented (Y/N) |
---|---|---|---|---|
1 | Updates a contract with valid auto renew period | contractId=<CREATED_CONTRACT_ID>, autoRenewPeriod="7000000", commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update succeeds and the contract has the new auto renew period. | Y |
2 | Updates a contract with minimum auto renew period | contractId=<CREATED_CONTRACT_ID>, autoRenewPeriod="6999999", commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update succeeds and the contract has the minimum auto renew period. | Y |
3 | Updates a contract with maximum auto renew period | contractId=<CREATED_CONTRACT_ID>, autoRenewPeriod="8000001", commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update succeeds and the contract has the maximum auto renew period. | Y |
4 | Updates a contract with auto renew period below minimum | contractId=<CREATED_CONTRACT_ID>, autoRenewPeriod="2591000", commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update fails with AUTORENEW_DURATION_NOT_IN_RANGE . | Y |
5 | Updates a contract with auto renew period above maximum | contractId=<CREATED_CONTRACT_ID>, autoRenewPeriod="9000000", commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update fails with AUTORENEW_DURATION_NOT_IN_RANGE . | Y |
6 | Updates a contract with auto renew period of zero | contractId=<CREATED_CONTRACT_ID>, autoRenewPeriod="0", commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update fails with INVALID_RENEWAL_PERIOD . | Y |
7 | Updates a contract with negative auto renew period | contractId=<CREATED_CONTRACT_ID>, autoRenewPeriod="-1", commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update fails with INVALID_RENEWAL_PERIOD . | Y |
8 | Updates a contract with auto renew period of 9,223,372,036,854,775,807 (int64 max) seconds | contractId=<CREATED_CONTRACT_ID>, autoRenewPeriod="9223372036854775807", commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update fails with AUTORENEW_DURATION_NOT_IN_RANGE . | Y |
9 | Updates a contract with auto renew period of -9,223,372,036,854,775,808 (int64 min) seconds | contractId=<CREATED_CONTRACT_ID>, autoRenewPeriod="-9223372036854775808", commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update fails with AUTORENEW_DURATION_NOT_IN_RANGE . | Y |
Expiration Time:
- The new expiration time for the contract in epoch seconds.
Test no | Name | Input | Expected response | Implemented (Y/N) |
---|---|---|---|---|
1 | Updates the expiration time of a contract to 0 seconds | contractId=<CREATED_CONTRACT_ID>, expirationTime="0", commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update fails with an INVALID_EXPIRATION_TIME response code from the network. | Y |
2 | Updates the expiration time of a contract to -1 seconds | contractId=<CREATED_CONTRACT_ID>, expirationTime="-1", commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update fails with an INVALID_EXPIRATION_TIME response code from the network. | Y |
3 | Updates the expiration time of a contract to a valid future time | contractId=<CREATED_CONTRACT_ID>, expirationTime=<CURRENT_TIME + 8000001>, commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update succeeds and the contract has the new expiration time. | Y |
4 | Updates the expiration time of a contract to 1 second less than its current expiration time | contractId=<CREATED_CONTRACT_ID>, expirationTime=<CURRENT_EXPIRATION_TIME - 1>, commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update fails with an EXPIRATION_REDUCTION_NOT_ALLOWED response code from the network. | Y |
5 | Updates the expiration time of a contract to 9,223,372,036,854,775,807 (int64 max) seconds | contractId=<CREATED_CONTRACT_ID>, expirationTime="9223372036854775807", commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update fails with an INVALID_EXPIRATION_TIME response code from the network. | Y |
6 | Updates the expiration time of a contract to -9,223,372,036,854,775,808 (int64 min) seconds | contractId=<CREATED_CONTRACT_ID>, expirationTime="-9223372036854775808", commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update fails with an INVALID_EXPIRATION_TIME response code from the network. | Y |
Memo:
- Short publicly visible memo about the contract.
Test no | Name | Input | Expected response | Implemented (Y/N) |
---|---|---|---|---|
1 | Updates a contract with valid memo | contractId=<CREATED_CONTRACT_ID>, memo="Updated contract memo", commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update succeeds and the contract has the new memo. | Y |
2 | Updates a contract with empty memo | contractId=<CREATED_CONTRACT_ID>, memo="", commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update succeeds and the contract has no memo. | Y |
3 | Updates a contract with memo at maximum length (100 bytes) | contractId=<CREATED_CONTRACT_ID>, memo=<100_BYTE_STRING>, commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update succeeds and the contract has the new memo. | Y |
4 | Updates a contract with memo exceeding maximum length | contractId=<CREATED_CONTRACT_ID>, memo=<101_BYTE_STRING>, commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update fails with MEMO_TOO_LONG . | Y |
5 | Updates a contract with memo containing null byte | contractId=<CREATED_CONTRACT_ID>, memo="Test\0memo", commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update fails with INVALID_ZERO_BYTE_IN_STRING . | Y |
6 | Updates a contract with memo containing only whitespace | contractId=<CREATED_CONTRACT_ID>, memo=" ", commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update succeeds and the contract has the whitespace memo. | Y |
7 | Updates a contract with memo containing special characters | contractId=<CREATED_CONTRACT_ID>, memo="!@#$%^&*()_+-=[]{};':",./<>?", commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update succeeds and the contract has the special character memo. | Y |
8 | Updates a contract with memo containing unicode characters | contractId=<CREATED_CONTRACT_ID>, memo="测试主题备注 🚀", commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update succeeds and the contract has the unicode memo. | Y |
AutoRenewAccount:
- Optional account to be used at the contract's expirationTime to extend the life of the contract.
Test no | Name | Input | Expected response | Implemented (Y/N) |
---|---|---|---|---|
1 | Updates a contract with valid auto renew account | contractId=<CREATED_CONTRACT_ID>, autoRenewAccountId=<VALID_ACCOUNT_ID>, commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update succeeds and the contract has the new auto renew account. | Y |
2 | Updates a contract with non-existent auto renew account | contractId=<CREATED_CONTRACT_ID>, autoRenewAccountId="0.0.999999", commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update fails with INVALID_AUTORENEW_ACCOUNT . | Y |
3 | Updates a contract with deleted auto renew account | contractId=<CREATED_CONTRACT_ID>, autoRenewAccountId=<DELETED_ACCOUNT_ID>, commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update fails with INVALID_AUTORENEW_ACCOUNT . | Y |
4 | Updates a contract to remove auto renew account by setting default account ID | contractId=<CREATED_CONTRACT_ID>, autoRenewAccountId="0.0.0", commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update succeeds and the contract has no auto renew account. | Y |
5 | Updates a contract with invalid auto renew account format | contractId=<CREATED_CONTRACT_ID>, autoRenewAccountId="invalid", commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update fails with an SDK internal error. | Y |
Max Automatic Associations
- The maximum number of tokens that can be auto-associated with this contract.
Test no | Name | Input | Expected response | Implemented (Y/N) |
---|---|---|---|---|
1 | Updates a contract with maxAutomaticTokenAssociations = 0 | contractId=<CREATED_CONTRACT_ID>, maxAutomaticTokenAssociations=0, commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update succeeds; contract has no auto associations. | Y |
2 | Updates a contract with maxAutomaticTokenAssociations = 10 | contractId=<CREATED_CONTRACT_ID>, maxAutomaticTokenAssociations=10, commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update succeeds; contract allows up to 10 auto associations. | Y |
3 | Updates a contract with maxAutomaticTokenAssociations = 1000 | contractId=<CREATED_CONTRACT_ID>, maxAutomaticTokenAssociations=1000, commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update succeeds; contract allows up to 1000 auto associations. | Y |
4 | Updates a contract with maxAutomaticTokenAssociations = -1 (no-limit per HIP-904) | contractId=<CREATED_CONTRACT_ID>, maxAutomaticTokenAssociations=-1, commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update succeeds; no limit on auto associations. | Y |
5 | Updates a contract with invalid negative maxAutomaticTokenAssociations = -2 | contractId=<CREATED_CONTRACT_ID>, maxAutomaticTokenAssociations=-2, commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update fails with INVALID_MAX_AUTO_ASSOCIATIONS . | Y |
6 | Updates a contract with maxAutomaticTokenAssociations = 2,147,483,647 | contractId=<CREATED_CONTRACT_ID>, maxAutomaticTokenAssociations=2147483647, commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update fails with REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT . | Y |
7 | Updates a contract with maxAutomaticTokenAssociations = -2,147,483,647 | contractId=<CREATED_CONTRACT_ID>, maxAutomaticTokenAssociations=-2147483647, commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update fails with INVALID_MAX_AUTO_ASSOCIATIONS . | Y |
Staked Account/Node ID:
- The account ID or node ID you would stake the contract account to earn staking rewards.
Test no | Name | Input | Expected response | Implemented (Y/N) |
---|---|---|---|---|
1 | Updates a contract that is staked to valid account ID | contractId=<CREATED_CONTRACT_ID>, stakedAccountId=<VALID_ACCOUNT_ID>, commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | Succeeds, contract staking reflected. | Y |
2 | Updates a contract that is staked to valid node ID | contractId=<CREATED_CONTRACT_ID>, stakedNodeId=<VALID_NODE_ID>, commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | Succeeds, staking reflected. | Y |
3 | Updates a contract that has an invalid stakedAccountId | contractId=<CREATED_CONTRACT_ID>, stakedAccountId="0.0.99999", commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | Fails with INVALID_STAKING_ID . | Y |
4 | Updates a contract that has an invalid stakedNodeId | contractId=<CREATED_CONTRACT_ID>, stakedNodeId="9999999", commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | Fails with INVALID_STAKING_ID . | Y |
5 | Updates a contract that tries to set both stakedAccountId and stakedNodeId present | contractId=<CREATED_CONTRACT_ID>, stakedAccountId=<VALID_ACCOUNT_ID>, stakedNodeId=<VALID_NODE_ID>, commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | Succeeds. | Y |
6 | Updates a contract that tries to stake to a deleted account ID | contractId=<CREATED_CONTRACT_ID>, stakedAccountId=<DELETED_ACCOUNT_ID>, commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | Fails with INVALID_STAKING_ID . | Y |
7 | Updates a contract to remove staking by setting default account ID | contractId=<CREATED_CONTRACT_ID>, stakedAccountId="0.0.0", commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | Succeeds, contract no longer staked. | Y |
8 | Updates a contract with an invalid negative node ID | contractId=<CREATED_CONTRACT_ID>, stakedNodeId="-100", commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | Fails with INVALID_STAKING_ID . | Y |
Decline Staking Reward:
- Whether the contract declines staking rewards.
Test no | Name | Input | Expected response | Implemented (Y/N) |
---|---|---|---|---|
1 | Updates the decline reward policy of a contract to decline staking rewards | contractId=<CREATED_CONTRACT_ID>, declineStakingReward=true, commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update succeeds and the contract declines staking rewards. | Y |
2 | Updates the decline reward policy of a contract to not decline staking rewards | contractId=<CREATED_CONTRACT_ID>, declineStakingReward=false, commonTransactionParams.signers=[<ADMIN_KEY_OF_CREATED_CONTRACT>] | The contract update succeeds and the contract doesn't decline staking rewards. | Y |
JSON Request Example
{
"jsonrpc": "2.0",
"id": 2,
"method": "updateContract",
"params": {
"contractId": "0.0.1234",
"memo": "Updated contract memo",
"autoRenewPeriod": "7776000",
"commonTransactionParams": {
"signers": [
"302E020100300506032B657004220420DE6788D0A09F20DED806F446C02FB929D8CD8D17022374AFB3739A1D50BA72C8"
]
}
}
}
JSON Response Example
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"status": "SUCCESS"
}
}