-
Notifications
You must be signed in to change notification settings - Fork 147
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(TCK): Add AccountCreate method (#2476)
* feat: Added TCK server json-rpc methods for account create transaction Signed-off-by: ivaylogarnev-limechain <[email protected]> * fix: Renamed some interfaces, refactored processKeyRecursively func, moved some functions outside Key method. Signed-off-by: ivaylogarnev-limechain <[email protected]> * fix: Splitted models folder into params/response and renamed the Input interface Signed-off-by: ivaylogarnev-limechain <[email protected]> * fix: Removed unnecessary async, introduced CustomError class, refacoted account method Signed-off-by: ivaylogarnev-limechain <[email protected]> * refactor: Removed logs, unnecessary error handling logic, refactored processKeyRecursively method Signed-off-by: ivaylogarnev-limechain <[email protected]> * refactor: Introduced asn1DecodedKey interface and JSONRPCErrorCode custom enum, refactored getEvmAddressFromKey, getKeyFromString, handleEd25519andEcdsa methods and SDK setup Signed-off-by: ivaylogarnev-limechain <[email protected]> --------- Signed-off-by: ivaylogarnev-limechain <[email protected]>
- Loading branch information
1 parent
d756199
commit 8885cf3
Showing
25 changed files
with
5,652 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
export enum AccountKey { | ||
ED25519_PRIVATE_KEY = "ed25519PrivateKey", | ||
ECDSA_SECP256K1_PRIVATE_KEY = "ecdsaSecp256k1PrivateKey", | ||
ED25519_PUBLIC_KEY = "ed25519PublicKey", | ||
ECDSA_SECP256K1_PUBLIC_KEY = "ecdsaSecp256k1PublicKey", | ||
KEY_LIST = "keyList", | ||
THRESHOLD_KEY = "thresholdKey", | ||
EVM_ADDRESS = "evmAddress", | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { sdk } from "./sdk_data"; | ||
import { RpcMethodParams } from "./params/sdk"; | ||
|
||
/** | ||
* Very primitive catch-all mapping prototype | ||
* @returns {Promise<*>} | ||
* @param {Input} input | ||
*/ | ||
export default async function mapMethods({ | ||
callClass, | ||
methods, | ||
}: RpcMethodParams): Promise<string> { | ||
const cl: any = (await import("@hashgraph/sdk"))[callClass]; | ||
|
||
let currentObject: any = new cl(); | ||
for (let { name, param } of methods) { | ||
if (param === "client") { | ||
param = sdk.getClient(); | ||
} | ||
|
||
if (typeof currentObject[name] === "function") { | ||
currentObject = await currentObject[name](param); | ||
} else if (typeof cl[name] === "function") { | ||
currentObject = await cl[name](param); | ||
} else if (typeof currentObject[name] === "object") { | ||
currentObject = await currentObject[name]; | ||
} else { | ||
throw Error(`${callClass}.${name}() isn't a function`); | ||
} | ||
} | ||
return currentObject; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import { AccountCreateTransaction, Hbar, AccountId } from "@hashgraph/sdk"; | ||
|
||
import { sdk } from "../sdk_data"; | ||
import { getKeyFromString } from "../utils/key"; | ||
import { CreateAccountResponse } from "../response/account"; | ||
|
||
import { CreateAccountParams } from "../params/account"; | ||
import { applyCommonTransactionParams } from "../params/common-tx-params"; | ||
|
||
export const createAccount = async ({ | ||
key, | ||
initialBalance, | ||
receiverSignatureRequired, | ||
maxAutoTokenAssociations, | ||
commonTransactionParams, | ||
stakedAccountId, | ||
stakedNodeId, | ||
declineStakingReward, | ||
memo, | ||
autoRenewPeriod, | ||
alias, | ||
}: CreateAccountParams): Promise<CreateAccountResponse> => { | ||
let transaction = new AccountCreateTransaction().setGrpcDeadline(30000); | ||
|
||
if (key != null) { | ||
transaction.setKey(getKeyFromString(key)); | ||
} | ||
|
||
if (initialBalance != null) { | ||
transaction.setInitialBalance(Hbar.fromTinybars(initialBalance)); | ||
} | ||
|
||
if (receiverSignatureRequired != null) { | ||
transaction.setReceiverSignatureRequired(receiverSignatureRequired); | ||
} | ||
|
||
if (maxAutoTokenAssociations != null) { | ||
transaction.setMaxAutomaticTokenAssociations(maxAutoTokenAssociations); | ||
} | ||
|
||
if (stakedAccountId != null) { | ||
const accountId = AccountId.fromString(stakedAccountId); | ||
|
||
transaction.setStakedAccountId(accountId); | ||
} | ||
|
||
if (stakedNodeId != null) { | ||
transaction.setStakedNodeId(stakedNodeId); | ||
} | ||
|
||
if (declineStakingReward != null) { | ||
transaction.setDeclineStakingReward(declineStakingReward); | ||
} | ||
|
||
if (memo != null) { | ||
transaction.setAccountMemo(memo); | ||
} | ||
|
||
if (autoRenewPeriod != null) { | ||
transaction.setAutoRenewPeriod(autoRenewPeriod); | ||
} | ||
|
||
if (alias != null) { | ||
transaction.setAlias(alias); | ||
} | ||
|
||
if (commonTransactionParams != null) { | ||
applyCommonTransactionParams( | ||
commonTransactionParams, | ||
transaction, | ||
sdk.getClient(), | ||
); | ||
} | ||
|
||
// Sign the transaction with the client operator private key if not already signed and submit to a Hedera network | ||
const txResponse = await transaction.execute(sdk.getClient()); | ||
const receipt = await txResponse.getReceipt(sdk.getClient()); | ||
|
||
return { | ||
accountId: receipt.accountId.toString(), | ||
status: receipt.status.toString(), | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import glob from "glob"; | ||
import path from "path"; | ||
|
||
// Require all files in this folder in one module.export | ||
|
||
let allMethods: Record<string, string> = {}; | ||
glob.sync(path.join(__dirname, "**/*.ts")).forEach((file) => { | ||
allMethods = { ...allMethods, ...require(path.resolve(file)) }; | ||
}); | ||
|
||
export default allMethods; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,208 @@ | ||
import { PrivateKey, KeyList, PublicKey } from "@hashgraph/sdk"; | ||
|
||
import { invalidParamError } from "../utils/invalid-param-error"; | ||
import { | ||
getKeyFromString, | ||
convertToKeyListHex, | ||
getEvmAddressFromKey, | ||
getKeyPairFromParams, | ||
} from "../utils/key"; | ||
|
||
import { AccountKey } from "../enums/account-key"; | ||
|
||
import { KeyGenerationParams } from "../params/key"; | ||
import { KeyGenerationResponse } from "../response/key"; | ||
|
||
export const generateKey = ( | ||
params: KeyGenerationParams, | ||
): KeyGenerationResponse => { | ||
const isValidFromKeyType = | ||
params.fromKey && | ||
![ | ||
AccountKey.ED25519_PUBLIC_KEY, | ||
AccountKey.ECDSA_SECP256K1_PUBLIC_KEY, | ||
AccountKey.EVM_ADDRESS, | ||
].includes(params.type as AccountKey); | ||
|
||
if (isValidFromKeyType) { | ||
return invalidParamError<KeyGenerationResponse>( | ||
"fromKey should only be provided for specific public key types or EVM address", | ||
); | ||
} | ||
|
||
const isValidThresholdParam = | ||
params.threshold && params.type !== AccountKey.THRESHOLD_KEY; | ||
|
||
if (isValidThresholdParam) { | ||
return invalidParamError<KeyGenerationResponse>( | ||
"threshold should only be provided for thresholdKey types", | ||
); | ||
} | ||
|
||
const isValidKeysParam = | ||
params.keys && | ||
![AccountKey.KEY_LIST, AccountKey.THRESHOLD_KEY].includes( | ||
params.type as AccountKey, | ||
); | ||
|
||
if (isValidKeysParam) { | ||
return invalidParamError<KeyGenerationResponse>( | ||
"keys should only be provided for keyList or thresholdKey types", | ||
); | ||
} | ||
|
||
const isValidThresholdKeysParam = | ||
(params.type === AccountKey.THRESHOLD_KEY || | ||
params.type === AccountKey.KEY_LIST) && | ||
!params.keys; | ||
|
||
if (isValidThresholdKeysParam) { | ||
return invalidParamError<KeyGenerationResponse>( | ||
"keys list is required for generating a KeyList type", | ||
); | ||
} | ||
|
||
const isValidThresholdParamForThresholdKey = | ||
params.type === AccountKey.THRESHOLD_KEY && params.threshold === 0; | ||
|
||
if (isValidThresholdParamForThresholdKey) { | ||
return invalidParamError<KeyGenerationResponse>( | ||
"threshold is required for generating a ThresholdKey type", | ||
); | ||
} | ||
|
||
const response: KeyGenerationResponse = { | ||
key: "", | ||
privateKeys: [], | ||
}; | ||
|
||
try { | ||
const { key, privateKeys } = processKeyRecursively( | ||
params, | ||
response, | ||
false, | ||
); | ||
|
||
response.key = key; | ||
response.privateKeys = privateKeys; | ||
} catch (error) { | ||
return invalidParamError<KeyGenerationResponse>(error.message); | ||
} | ||
|
||
return response; | ||
}; | ||
|
||
const handleED25519PrivateKey = ( | ||
response: KeyGenerationResponse, | ||
params?: KeyGenerationParams, | ||
isList?: boolean, | ||
) => { | ||
const ed25519PrivateKey = PrivateKey.generateED25519(); | ||
if (isList) response.privateKeys.push(ed25519PrivateKey.toStringDer()); | ||
return { | ||
key: ed25519PrivateKey.toStringDer(), | ||
privateKeys: response.privateKeys, | ||
}; | ||
}; | ||
|
||
const handleECDSA_SECP256K1PrivateKey = ( | ||
response: KeyGenerationResponse, | ||
params?: KeyGenerationParams, | ||
isList?: boolean, | ||
) => { | ||
const ecdsaPrivateKey = PrivateKey.generateECDSA(); | ||
if (isList) response.privateKeys.push(ecdsaPrivateKey.toStringDer()); | ||
return { | ||
key: ecdsaPrivateKey.toStringDer(), | ||
privateKeys: response.privateKeys, | ||
}; | ||
}; | ||
|
||
const handleEd25519andEcdsaPublicKey = ( | ||
response: KeyGenerationResponse, | ||
params?: KeyGenerationParams, | ||
isList?: boolean, | ||
): KeyGenerationResponse => { | ||
const { privateKey, publicKey } = getKeyPairFromParams(params); | ||
|
||
const privateKeyDer = privateKey.toStringDer(); | ||
const publicKeyDer = publicKey.toStringDer(); | ||
|
||
if (isList) { | ||
response.privateKeys.push(privateKeyDer); | ||
} | ||
|
||
return { key: publicKeyDer, privateKeys: response.privateKeys }; | ||
}; | ||
|
||
const handleKeyList = ( | ||
response: KeyGenerationResponse, | ||
params?: KeyGenerationParams, | ||
) => { | ||
const keyList = new KeyList(); | ||
|
||
for (const keyParam of params.keys!) { | ||
const { key: keyStr } = processKeyRecursively(keyParam, response, true); | ||
const key = getKeyFromString(keyStr); | ||
if (key) { | ||
keyList.push(key); | ||
} | ||
} | ||
if (params.type === AccountKey.THRESHOLD_KEY) { | ||
keyList.setThreshold(params.threshold!); | ||
} | ||
|
||
return { | ||
key: convertToKeyListHex(keyList) || "", | ||
privateKeys: response.privateKeys, | ||
}; | ||
}; | ||
|
||
const handleEvmAddress = ( | ||
response: KeyGenerationResponse, | ||
params?: KeyGenerationParams, | ||
isList?: boolean, | ||
): KeyGenerationResponse => { | ||
if (params.fromKey) { | ||
return getEvmAddressFromKey(params.fromKey, response); | ||
} | ||
|
||
// Generate new EVM address if fromKey is not provided | ||
const privateKey = PrivateKey.generateECDSA(); | ||
return { | ||
key: privateKey.publicKey.toEvmAddress(), | ||
privateKeys: [privateKey.toStringDer()], | ||
}; | ||
}; | ||
|
||
const keyHandlers: { | ||
[key: string]: ( | ||
response: KeyGenerationResponse, | ||
params?: KeyGenerationParams, | ||
isList?: boolean, | ||
) => KeyGenerationResponse; | ||
} = { | ||
[AccountKey.ED25519_PRIVATE_KEY]: handleED25519PrivateKey, | ||
[AccountKey.ECDSA_SECP256K1_PRIVATE_KEY]: handleECDSA_SECP256K1PrivateKey, | ||
[AccountKey.ED25519_PUBLIC_KEY]: handleEd25519andEcdsaPublicKey, | ||
[AccountKey.ECDSA_SECP256K1_PUBLIC_KEY]: handleEd25519andEcdsaPublicKey, | ||
[AccountKey.KEY_LIST]: handleKeyList, | ||
[AccountKey.THRESHOLD_KEY]: handleKeyList, | ||
[AccountKey.EVM_ADDRESS]: handleEvmAddress, | ||
}; | ||
|
||
const processKeyRecursively = ( | ||
params: KeyGenerationParams, | ||
response: KeyGenerationResponse, | ||
isList: boolean, | ||
): KeyGenerationResponse => { | ||
const handleKey = keyHandlers[params.type]; | ||
|
||
if (handleKey) { | ||
return handleKey(response, params, isList); | ||
} else { | ||
return invalidParamError<KeyGenerationResponse>( | ||
"key type not recognized", | ||
); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { Client, AccountId } from "@hashgraph/sdk"; | ||
|
||
import { sdk } from "../sdk_data"; | ||
import { SdkResponse } from "../response/sdk"; | ||
import { SdkSetupParams } from "../params/sdk"; | ||
|
||
export default { | ||
setup: ({ | ||
operatorAccountId, | ||
operatorPrivateKey, | ||
nodeIp, | ||
nodeAccountId, | ||
mirrorNetworkIp, | ||
}: SdkSetupParams): SdkResponse => { | ||
let client: Client; | ||
|
||
if (nodeIp && nodeAccountId && mirrorNetworkIp) { | ||
const node = { [nodeIp]: AccountId.fromString(nodeAccountId) }; | ||
client = Client.forNetwork(node); | ||
} else { | ||
client = Client.forTestnet(); | ||
} | ||
|
||
client.setOperator(operatorAccountId, operatorPrivateKey); | ||
client.setRequestTimeout(30000); | ||
|
||
sdk.client = client; | ||
|
||
return { | ||
message: `Successfully setup ${client} client.`, | ||
status: "SUCCESS", | ||
}; | ||
}, | ||
reset: (): SdkResponse => { | ||
sdk.client = null; | ||
return { status: "SUCCESS" }; | ||
}, | ||
}; |
Oops, something went wrong.