-
Notifications
You must be signed in to change notification settings - Fork 17
/
ModuleClient.ts
168 lines (160 loc) · 6.36 KB
/
ModuleClient.ts
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
import { ContractTransactionMetadata } from '../GenericContract.js';
import * as ModuleReference from './ModuleReference.js';
import * as BlockHash from './BlockHash.js';
import * as Parameter from './Parameter.js';
import * as TransactionHash from './TransactionHash.js';
import * as ContractName from './ContractName.js';
import {
AccountTransactionType,
InitContractPayload,
VersionedModuleSource,
} from '../types.js';
import { ConcordiumGRPCClient } from '../grpc/index.js';
import { AccountSigner, signTransaction } from '../signHelpers.js';
import * as CcdAmount from './CcdAmount.js';
import * as TransactionExpiry from './TransactionExpiry.js';
/**
* An update transaction without header.
*/
export type ContractInitTransaction = {
/** The type of the transaction, which will always be of type {@link AccountTransactionType.InitContract} */
type: AccountTransactionType.InitContract;
/** The payload of the transaction, which will always be of type {@link InitContractPayload} */
payload: InitContractPayload;
};
/**
* Internal class representing a smart contract module deployed on chain.
*
* The public type for this {@link ModuleClient} is exported separately to ensure
* the constructor is only available from within this module.
*/
class ModuleClient {
/** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */
private __nominal = true;
constructor(
/** The gRPC connection used by this object */
public readonly grpcClient: ConcordiumGRPCClient,
/** The reference for this module */
public readonly moduleReference: ModuleReference.Type
) {}
}
/**
* Type representing a smart contract module deployed on chain.
*/
export type Type = ModuleClient;
export function instanceOf(value: unknown): value is ModuleClient {
return value instanceof ModuleClient;
}
/**
* Create a new `GenericModule` instance for interacting with a smart contract module on chain.
* The caller must ensure that the smart contract module is already deployed on chain.
*
* @param {ConcordiumGRPCClient} grpcClient - The GRPC client for accessing a node.
* @param {ModuleReference} moduleReference - The reference of the deployed smart contract module.
*
* @returns {ModuleClient}
*/
export function createUnchecked(
grpcClient: ConcordiumGRPCClient,
moduleReference: ModuleReference.Type
): ModuleClient {
return new ModuleClient(grpcClient, moduleReference);
}
/**
* Create a new `GenericModule` instance for interacting with a smart contract module on chain.
* This function ensures the module is already deployed on chain otherwise produces an error.
*
* @param {ConcordiumGRPCClient} grpcClient - The GRPC client for accessing a node.
* @param {ModuleReference} moduleReference - The reference of the deployed smart contract module.
*
* @throws If failing to communicate with the concordium node or module reference does not correspond to a module on chain.
*
* @returns {ModuleClient}
*/
export async function create(
grpcClient: ConcordiumGRPCClient,
moduleReference: ModuleReference.Type
): Promise<ModuleClient> {
const mod = new ModuleClient(grpcClient, moduleReference);
await checkOnChain(mod);
return mod;
}
/**
* Check if this module is deployed to the chain.
*
* @param {ModuleClient} moduleClient The client for a smart contract module on chain.
* @param {BlockHash.Type} [blockHash] Hash of the block to check information at. When not provided the last finalized block is used.
*
* @throws {RpcError} If failing to communicate with the concordium node or module is not deployed on chain.
* @returns {boolean} Indicating whether the module is deployed on chain.
*/
export async function checkOnChain(
moduleClient: ModuleClient,
blockHash?: BlockHash.Type
): Promise<void> {
await getModuleSource(moduleClient, blockHash);
}
/**
* Get the module source of the deployed smart contract module.
*
* @param {ModuleClient} moduleClient The client for a smart contract module on chain.
* @param {BlockHash.Type} [blockHash] Hash of the block to check information at. When not provided the last finalized block is used.
*
* @throws {RpcError} If failing to communicate with the concordium node or module not found.
* @returns {VersionedModuleSource} Module source of the deployed smart contract module.
*/
export function getModuleSource(
moduleClient: ModuleClient,
blockHash?: BlockHash.Type
): Promise<VersionedModuleSource> {
return moduleClient.grpcClient.getModuleSource(
moduleClient.moduleReference,
blockHash
);
}
/**
* Creates and sends transaction for initializing a smart contract `contractName` with parameter `input`.
*
* @param {ModuleClient} moduleClient The client for a smart contract module on chain.
* @param {ContractName.Type} contractName - The name of the smart contract to instantiate (this is without the `init_` prefix).
* @param {ContractTransactionMetadata} metadata - Metadata to be used for the transaction (with defaults).
* @param {Parameter.Type} parameter - Input for for contract function.
* @param {AccountSigner} signer - An object to use for signing the transaction.
*
* @throws If the query could not be invoked successfully.
*
* @returns {TransactionHash.Type} The transaction hash of the update transaction.
*/
export async function createAndSendInitTransaction(
moduleClient: ModuleClient,
contractName: ContractName.Type,
metadata: ContractTransactionMetadata,
parameter: Parameter.Type,
signer: AccountSigner
): Promise<TransactionHash.Type> {
const payload: InitContractPayload = {
moduleRef: moduleClient.moduleReference,
amount: metadata.amount ?? CcdAmount.zero(),
initName: contractName,
maxContractExecutionEnergy: metadata.energy,
param: parameter,
};
const { nonce } = await moduleClient.grpcClient.getNextAccountNonce(
metadata.senderAddress
);
const header = {
expiry: metadata.expiry ?? TransactionExpiry.futureMinutes(5),
nonce: nonce,
sender: metadata.senderAddress,
};
const transaction = {
type: AccountTransactionType.InitContract,
header,
payload,
};
const signature = await signTransaction(transaction, signer);
return moduleClient.grpcClient.sendAccountTransaction(
transaction,
signature
);
}