Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 3x 5x 5x 1x 1x 4x 4x 4x 4x 3x 4x 1x 1x 1x 1x 1x 1x 1x 2x 2x 2x 2x 2x 2x 2x 1x 1x 1x 2x 2x 2x 2x 1x 2x 3x 3x 3x 1x 1x 1x 2x 2x 2x 2x 1x 1x 1x 2x 2x 2x 1x | // SPDX-License-Identifier: Apache-2.0
import { TokenId, TokenType } from '@hashgraph/sdk';
import { IOBserver } from '../controller/IObserver';
import { LoggerService } from '../services/LoggerService';
import { ServiceLocator } from '../services/ServiceLocator';
import { IState } from './IState';
import { CLIService } from '../services/CLIService';
import { ClientService } from '../services/ClientService';
import { accounts, tokens } from '../configuration/initialResources.json';
import { EventType } from '../types/EventType';
import { TokenUtils } from '../utils/TokenUtils';
import { ITokenProps } from '../configuration/types/ITokenProps';
import { IAccountProps } from '../configuration/types/IAccountProps';
import { AccountUtils } from '../utils/AccountUtils';
import { LOADING, RESOURCE_CREATION_STATE_INIT_MESSAGE } from '../constants';
/**
* Represents the state of resource creation.
* This class is responsible for initializing the ResourceCreationState object.
*
* Uses {@link accounts} and {@link tokens} from the initialResources.json
* to create initial resources for the local-node environment
*
* @implements {IState}
*/
export class ResourceCreationState implements IState {
/**
* The name of the state.
*/
private readonly stateName: string;
/**
* The logger used for logging resource creation state information.
*/
private readonly logger: LoggerService;
/**
* The CLI service used for resource creation.
*/
private readonly cliService: CLIService;
/**
* The client service used for resource creation.
*/
private readonly clientService: ClientService;
/**
* The observer for the resource creation state.
*/
private observer: IOBserver | undefined;
/**
* Represents the state of resource creation.
* This class is responsible for initializing the ResourceCreationState object.
*/
constructor() {
this.stateName = ResourceCreationState.name;
this.logger = ServiceLocator.Current.get<LoggerService>(LoggerService.name);
this.cliService = ServiceLocator.Current.get<CLIService>(CLIService.name);
this.clientService = ServiceLocator.Current.get<ClientService>(ClientService.name);
this.logger.trace(RESOURCE_CREATION_STATE_INIT_MESSAGE, this.stateName);
}
/**
* Subscribes an observer to receive updates from the ResourceCreationState.
* @param {IOBserver} observer The observer to subscribe.
*/
public subscribe(observer: IOBserver): void {
this.observer = observer;
}
/**
* This method is responsible for starting the ResourceCreationState.
* It creates tokens asynchronously or synchronously based on the CLI arguments.
* @returns Promise that resolves when the state is started.
* @emits {EventType.Finish} When the state is finished.
*/
public async onStart(): Promise<void> {
const { async, createInitialResources } = this.cliService.getCurrentArgv();
if (!createInitialResources) {
this.observer!.update(EventType.Finish);
return;
}
const mode = async ? 'asynchronous' : 'synchronous';
this.logger.info(
`${LOADING} Starting Resource Creation state in ${mode} mode...`, this.stateName);
const promise = this.createResources();
if (!async) {
await promise;
}
this.observer!.update(EventType.Finish);
}
/**
* Creates accounts and tokens with the given properties and associates them.
* @returns Promise that resolves when all resources are created.
*/
private async createResources(): Promise<void> {
const accountProps: IAccountProps[] = accounts as IAccountProps[];
const tokenProps: ITokenProps[] = tokens as ITokenProps[];
const tokenIds: Map<string, TokenId> = await this.createTokens(tokenProps);
await this.createAndAssociateAccounts(accountProps, tokenIds);
await this.mintTokens(tokenProps, tokenIds);
}
/**
* Creates accounts with the given properties.
* @param accountProps The properties of the accounts to create.
* @param tokenIdsBySymbol Map of token symbols to token IDs.
* @returns Promise that resolves when all accounts are created (and associated with tokens).
*/
private async createAndAssociateAccounts(accountProps: IAccountProps[],
tokenIdsBySymbol: Map<string, TokenId>): Promise<void> {
this.logger.info('Creating accounts', this.stateName);
const client = this.clientService.getClient();
const promises = accountProps.map(async (account: IAccountProps): Promise<void> => {
const { privateKey, accountInfo } = await AccountUtils.createAccountFromProps(account, client);
this.logger.info(
`Successfully created account with:
* normal account ID: ${accountInfo.accountId.toString()}
* aliased account ID: ${accountInfo.aliasKey ? `0.0.${ accountInfo.aliasKey?.toString()}` : 'N/A'}
* private key (use this in SDK/Hedera-native wallets): ${privateKey.toStringDer()}
* raw private key (use this for JSON RPC wallet import): ${privateKey.toStringRaw()}`,
this.stateName);
if (account.associatedTokens && account.associatedTokens.length > 0) {
const associatedTokenIds = this.getTokenIdsFor(account.associatedTokens, tokenIdsBySymbol);
await TokenUtils.associateAccountWithTokens(accountInfo.accountId, associatedTokenIds, privateKey, client);
this.logger.info(
`Associated account ${accountInfo.accountId} with tokens: ${associatedTokenIds.join(', ')}`,
this.stateName
);
}
});
await Promise.all(promises);
}
/**
* Creates tokens with the given properties.
* @param tokenProps The properties of the tokens to create.
* @returns Promise that resolves with a map of token symbols to token IDs.
*/
private async createTokens(tokenProps: ITokenProps[]): Promise<Map<string, TokenId>> {
this.logger.info('Creating tokens', this.stateName);
const client = this.clientService.getClient();
const promises = tokenProps.map(async (token: ITokenProps): Promise<[string, TokenId]> => {
const tokenId = await TokenUtils.createToken(token, client);
this.logger.info(
`Successfully created ${token.tokenType} token '${token.tokenSymbol}' with ID ${tokenId}`,
this.stateName
);
return [token.tokenSymbol, tokenId];
});
return new Map<string, TokenId>(await Promise.all(promises));
}
/**
* Gets the token IDs associated with the given token symbols.
* @param tokenSymbols The token symbols to get IDs for.
* @param tokenIdsBySymbol Map of token symbols to token IDs.
* @returns The token IDs associated with the account.
*/
private getTokenIdsFor(tokenSymbols: string[],
tokenIdsBySymbol: Map<string, TokenId>): TokenId[] {
return tokenSymbols?.filter(tokenSymbol => {
Iif (!tokenIdsBySymbol.has(tokenSymbol)) {
this.logger.warn(`Token ID for ${tokenSymbol} not found`, this.stateName);
return false;
}
return true;
}).map(tokenSymbol => tokenIdsBySymbol.get(tokenSymbol)!) || [];
}
/**
* Mints tokens with the given properties.
* @param tokenProps The properties of the tokens to mint.
* @param tokenIdsBySymbol Map of token symbols to token IDs.
*/
private async mintTokens(tokenProps: ITokenProps[],
tokenIdsBySymbol: Map<string, TokenId>): Promise<void> {
this.logger.info('Minting NFTs', this.stateName);
const client = this.clientService.getClient();
const promises = tokenProps
.filter(token => {
const isNft = token.tokenType === TokenType.NonFungibleUnique.toString();
const shouldMint = isNft && !!token.mints?.length;
Iif (shouldMint && !tokenIdsBySymbol.has(token.tokenSymbol)) {
this.logger.warn(`Token ID for ${token.tokenSymbol} not found`, this.stateName);
return false;
}
return shouldMint;
})
.map(async (token: ITokenProps): Promise<void> => {
const tokenId = tokenIdsBySymbol.get(token.tokenSymbol)!;
const supplyKey = TokenUtils.getSupplyKey(token);
await Promise.all(token.mints!.map(async ({ CID }) => {
await TokenUtils.mintToken(tokenId, CID, supplyKey, client);
this.logger.info(
`Minted token ID ${tokenId} with CID '${CID}'`,
this.stateName
);
}));
});
await Promise.all(promises);
}
}
|