-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathdynamic.ts
More file actions
105 lines (87 loc) · 3.44 KB
/
Copy pathdynamic.ts
File metadata and controls
105 lines (87 loc) · 3.44 KB
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
import {ICManagementCanister} from '@dfinity/ic-management';
import {Principal} from '@dfinity/principal';
import {fromNullable, isNullish, nonNullish, uint8ArrayToHexString} from '@dfinity/utils';
import kleur from 'kleur';
import {existsSync} from 'node:fs';
import {DEV_SATELLITE} from '../../constants/dev.constants';
import type {CliContext} from '../../types/context';
import type {InitDynamicModuleResult, ModuleCanisterId} from '../../types/module';
import {SATELLITE, SatelliteModule} from './index';
const {red, cyan} = kleur;
interface SatelliteDynamicModuleRegisterParams {
context: CliContext;
canisterId: ModuleCanisterId;
}
class SatelliteDynamicModule extends SatelliteModule {
async register({context, canisterId}: SatelliteDynamicModuleRegisterParams): Promise<void> {
const {state} = context;
const metadata = state.getModule(this.key);
// We check the canisterId in case the developer update their configuration at runtime.
if (nonNullish(metadata) && metadata.canisterId === canisterId) {
// Satellite has already been registered and initialized once. We do not need to add it to the state again.
return;
}
// We gather the current hash of the canister by fetching the canister status with th IC mgmt.
// This is useful otherwise dev would have to build twice to make the watcher notice the hash is really different and upgrade the satellite.
// Plus, this allows to preventively checks if the main identity is a controller of the Satellite.
let hash: string | undefined = undefined;
try {
hash = await this.currentModuleHash({context, canisterId});
} catch (err: unknown) {
console.log(red('️‼️ Unexpected error while fetching current module hash:'), err);
throw err;
}
if (isNullish(hash)) {
const err = new Error(
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-conversion
`‼️ The module hash for ${SATELLITE.name}, ID: ${cyan(canisterId.toString())} is undefined.`
);
console.log(red(err.message));
throw err;
}
state.saveModule({
key: this.key,
name: this.name,
canisterId,
// Deployed because the Satellite was created by the dev with the Console UI
status: 'deployed',
hash
});
}
private async currentModuleHash({
context,
canisterId
}: SatelliteDynamicModuleRegisterParams): Promise<string | undefined> {
const {agent} = context;
const {canisterStatus} = ICManagementCanister.create({
agent
});
const {module_hash} = await canisterStatus(Principal.from(canisterId));
const installedHash = fromNullable(module_hash);
if (isNullish(installedHash)) {
return undefined;
}
return uint8ArrayToHexString(installedHash);
}
}
export const initSatelliteDynamicModule = async ({
context,
satelliteId
}: {
context: CliContext;
satelliteId: ModuleCanisterId;
}): Promise<InitDynamicModuleResult<SatelliteDynamicModule>> => {
const mod = new SatelliteDynamicModule({
...SATELLITE,
canisterId: satelliteId,
...(existsSync(DEV_SATELLITE) && {wasmPath: DEV_SATELLITE})
});
try {
// The Satellite is not attached to a static canister ID and is created by the DEV in the Console.
// That's why we need to register it in the state as if it was deployed - which it is.
await mod.register({context, canisterId: satelliteId});
} catch (err: unknown) {
return {err};
}
return {mod};
};