-
Notifications
You must be signed in to change notification settings - Fork 16
/
useContractSelector.ts
131 lines (115 loc) · 3.34 KB
/
useContractSelector.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
import {
AccountAddress,
CcdAmount,
ConcordiumGRPCClient,
ContractAddress,
ContractName,
ReceiveName,
} from '@concordium/web-sdk';
import { useEffect, useState } from 'react';
import { errorString } from './error';
/**
* Data and state of a smart contract.
*/
export interface Info {
/**
* Version of the contract's semantics.
*/
version: number;
/**
* The contract's index on the chain.
*/
index: bigint;
/**
* The contract's name without the "init_" prefix.
*/
name: string;
/**
* The contract's balance.
*/
amount: CcdAmount.Type;
/**
* The address of the account that owns the contract.
*/
owner: AccountAddress.Type;
/**
* The contract's invokable methods.
*/
methods: string[];
/**
* The reference identifier of the contract's module.
*/
moduleRef: string;
}
async function refresh(rpc: ConcordiumGRPCClient, index: bigint): Promise<Info> {
const info = await rpc.getInstanceInfo(ContractAddress.create(index, BigInt(0)));
if (!info) {
throw new Error(`contract ${index} not found`);
}
const { version, name, owner, amount, methods, sourceModule } = info;
return {
version,
index,
name: ContractName.fromInitName(name).value,
amount,
owner,
methods: methods.map(ReceiveName.toString),
moduleRef: sourceModule.moduleRef,
};
}
function parseContractIndex(input: string) {
try {
return BigInt(input);
} catch (e) {
throw new Error(`invalid contract index '${input}'`);
}
}
async function loadContract(rpc: ConcordiumGRPCClient, input: string) {
const index = parseContractIndex(input);
return refresh(rpc, index);
}
/**
* A {@link useContractSelector} instance.
*/
export interface ContractSelector {
/**
* The selected contract info, if available.
* Is undefined if there isn't any index to look up, during lookup, or the lookup failed.
* In the latter case {@link error} will be non-empty.
*/
selected: Info | undefined;
/**
* Indicator of whether the lookup is in progress.
*/
isLoading: boolean;
/**
* Error parsing the input string or RPC error looking up the contract.
*/
error: string;
}
/**
* React hook to look up a smart contract's data and state from its index.
* @param rpc gRPC client through which to perform the lookup.
* @param input The index of the contract to look up.
* @return The resolved contract and related state.
*/
export function useContractSelector(rpc: ConcordiumGRPCClient | undefined, input: string): ContractSelector {
const [selected, setSelected] = useState<Info>();
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState('');
useEffect(() => {
setSelected(undefined);
setError('');
if (rpc && input) {
setIsLoading(true);
loadContract(rpc, input)
.then(setSelected)
.catch((err) => {
setError(errorString(err));
setSelected(undefined); // prevents race condition against an ongoing successful query
})
.finally(() => setIsLoading(false));
}
}, [rpc, input]);
return { selected, isLoading, error };
}