@keplr-wallet/hooks-starknet 0.12.194 → 0.12.195-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/tx/index.d.ts +1 -0
- package/build/tx/index.js +1 -0
- package/build/tx/index.js.map +1 -1
- package/build/tx/name-service-starknet-id.d.ts +37 -0
- package/build/tx/name-service-starknet-id.js +287 -0
- package/build/tx/name-service-starknet-id.js.map +1 -0
- package/build/tx/name-service.d.ts +20 -0
- package/build/tx/name-service.js +20 -0
- package/build/tx/name-service.js.map +1 -0
- package/build/tx/recipient.d.ts +16 -18
- package/build/tx/recipient.js +52 -237
- package/build/tx/recipient.js.map +1 -1
- package/build/tx/types.d.ts +15 -6
- package/package.json +14 -14
- package/src/tx/index.ts +1 -0
- package/src/tx/name-service-starknet-id.ts +326 -0
- package/src/tx/name-service.ts +39 -0
- package/src/tx/recipient.ts +77 -307
- package/src/tx/types.ts +18 -6
package/src/tx/recipient.ts
CHANGED
@@ -1,120 +1,16 @@
|
|
1
1
|
import {
|
2
2
|
IRecipientConfig,
|
3
|
-
IRecipientConfigWithStarknetID,
|
4
3
|
UIProperties,
|
4
|
+
IRecipientConfigWithNameServices,
|
5
5
|
} from "./types";
|
6
6
|
import { TxChainSetter } from "./chain";
|
7
7
|
import { ChainGetter } from "@keplr-wallet/stores";
|
8
|
-
import {
|
9
|
-
|
10
|
-
computed,
|
11
|
-
makeObservable,
|
12
|
-
observable,
|
13
|
-
runInAction,
|
14
|
-
} from "mobx";
|
15
|
-
import {
|
16
|
-
EmptyAddressError,
|
17
|
-
InvalidHexError,
|
18
|
-
StarknetIDFailedToFetchError,
|
19
|
-
StarknetIDIsFetchingError,
|
20
|
-
} from "./errors";
|
8
|
+
import { action, computed, makeObservable, observable } from "mobx";
|
9
|
+
import { EmptyAddressError, InvalidHexError } from "./errors";
|
21
10
|
import { useState } from "react";
|
22
|
-
import { Buffer } from "buffer
|
23
|
-
import {
|
24
|
-
import {
|
25
|
-
|
26
|
-
interface StarknetIDFetchData {
|
27
|
-
isFetching: boolean;
|
28
|
-
starknetHexAddress?: string;
|
29
|
-
error?: Error;
|
30
|
-
}
|
31
|
-
|
32
|
-
const networkToNamingContractAddress = {
|
33
|
-
[constants.NetworkName.SN_MAIN]:
|
34
|
-
"0x6ac597f8116f886fa1c97a23fa4e08299975ecaf6b598873ca6792b9bbfb678",
|
35
|
-
[constants.NetworkName.SN_SEPOLIA]:
|
36
|
-
"0x0707f09bc576bd7cfee59694846291047e965f4184fe13dac62c56759b3b6fa7",
|
37
|
-
};
|
38
|
-
|
39
|
-
const basicAlphabet = "abcdefghijklmnopqrstuvwxyz0123456789-";
|
40
|
-
const basicSizePlusOne = BigInt(basicAlphabet.length + 1);
|
41
|
-
const bigAlphabet = "这来";
|
42
|
-
const basicAlphabetSize = BigInt(basicAlphabet.length);
|
43
|
-
const bigAlphabetSize = BigInt(bigAlphabet.length);
|
44
|
-
|
45
|
-
// Reference: https://github.com/lfglabs-dev/starknetid.js/blob/main/packages/core/src/utils.ts
|
46
|
-
function isStarkDomain(domain: string): boolean {
|
47
|
-
return /^(?:[a-z0-9-]{1,48}(?:[a-z0-9-]{1,48}[a-z0-9-])?\.)*[a-z0-9-]{1,48}\.stark$/.test(
|
48
|
-
domain
|
49
|
-
);
|
50
|
-
}
|
51
|
-
|
52
|
-
function encodeDomain(domain: string | undefined | null): bigint[] {
|
53
|
-
if (!domain) return [BigInt(0)];
|
54
|
-
|
55
|
-
const encoded = [];
|
56
|
-
for (const subdomain of domain.replace(".stark", "").split("."))
|
57
|
-
encoded.push(encode(subdomain));
|
58
|
-
return encoded;
|
59
|
-
}
|
60
|
-
|
61
|
-
function extractStars(str: string): [string, number] {
|
62
|
-
let k = 0;
|
63
|
-
while (str.endsWith(bigAlphabet[bigAlphabet.length - 1])) {
|
64
|
-
str = str.substring(0, str.length - 1);
|
65
|
-
k += 1;
|
66
|
-
}
|
67
|
-
return [str, k];
|
68
|
-
}
|
69
|
-
|
70
|
-
function encode(decoded: string | undefined): bigint {
|
71
|
-
let encoded = BigInt(0);
|
72
|
-
let multiplier = BigInt(1);
|
73
|
-
|
74
|
-
if (!decoded) {
|
75
|
-
return encoded;
|
76
|
-
}
|
77
|
-
|
78
|
-
if (decoded.endsWith(bigAlphabet[0] + basicAlphabet[1])) {
|
79
|
-
const [str, k] = extractStars(decoded.substring(0, decoded.length - 2));
|
80
|
-
decoded = str + bigAlphabet[bigAlphabet.length - 1].repeat(2 * (k + 1));
|
81
|
-
} else {
|
82
|
-
const [str, k] = extractStars(decoded);
|
83
|
-
if (k)
|
84
|
-
decoded =
|
85
|
-
str + bigAlphabet[bigAlphabet.length - 1].repeat(1 + 2 * (k - 1));
|
86
|
-
}
|
87
|
-
|
88
|
-
for (let i = 0; i < decoded.length; i += 1) {
|
89
|
-
const char = decoded[i];
|
90
|
-
const index = basicAlphabet.indexOf(char);
|
91
|
-
const bnIndex = BigInt(basicAlphabet.indexOf(char));
|
92
|
-
|
93
|
-
if (index !== -1) {
|
94
|
-
// add encoded + multiplier * index
|
95
|
-
if (i === decoded.length - 1 && decoded[i] === basicAlphabet[0]) {
|
96
|
-
encoded += multiplier * basicAlphabetSize;
|
97
|
-
multiplier *= basicSizePlusOne;
|
98
|
-
// add 0
|
99
|
-
multiplier *= basicSizePlusOne;
|
100
|
-
} else {
|
101
|
-
encoded += multiplier * bnIndex;
|
102
|
-
multiplier *= basicSizePlusOne;
|
103
|
-
}
|
104
|
-
} else if (bigAlphabet.indexOf(char) !== -1) {
|
105
|
-
// add encoded + multiplier * (basicAlphabetSize)
|
106
|
-
encoded += multiplier * basicAlphabetSize;
|
107
|
-
multiplier *= basicSizePlusOne;
|
108
|
-
// add encoded + multiplier * index
|
109
|
-
const newid =
|
110
|
-
(i === decoded.length - 1 ? 1 : 0) + bigAlphabet.indexOf(char);
|
111
|
-
encoded += multiplier * BigInt(newid);
|
112
|
-
multiplier *= bigAlphabetSize;
|
113
|
-
}
|
114
|
-
}
|
115
|
-
|
116
|
-
return encoded;
|
117
|
-
}
|
11
|
+
import { Buffer } from "buffer";
|
12
|
+
import { NameService } from "./name-service";
|
13
|
+
import { StarknetIdNameService } from "./name-service-starknet-id";
|
118
14
|
|
119
15
|
function isStarknetHexAddress(address: string): boolean {
|
120
16
|
if (!address.startsWith("0x")) {
|
@@ -135,179 +31,96 @@ function isStarknetHexAddress(address: string): boolean {
|
|
135
31
|
|
136
32
|
export class RecipientConfig
|
137
33
|
extends TxChainSetter
|
138
|
-
implements IRecipientConfig,
|
34
|
+
implements IRecipientConfig, IRecipientConfigWithNameServices
|
139
35
|
{
|
140
36
|
@observable
|
141
37
|
protected _value: string = "";
|
142
38
|
|
143
|
-
|
144
|
-
|
145
|
-
protected _starknetID:
|
146
|
-
| {
|
147
|
-
networkName: string;
|
148
|
-
namingContractAddress: string;
|
149
|
-
}
|
150
|
-
| undefined = undefined;
|
39
|
+
@observable
|
40
|
+
protected _preferredNameService: string | undefined = undefined;
|
151
41
|
|
152
|
-
|
153
|
-
|
154
|
-
protected _starknetIDFetchDataMap = new Map<string, StarknetIDFetchData>();
|
42
|
+
@observable.ref
|
43
|
+
protected nameServices: NameService[] = [];
|
155
44
|
|
156
45
|
constructor(chainGetter: ChainGetter, initialChainId: string) {
|
157
46
|
super(chainGetter, initialChainId);
|
158
47
|
|
48
|
+
this.nameServices.push(new StarknetIdNameService(this, chainGetter));
|
49
|
+
|
159
50
|
makeObservable(this);
|
160
51
|
}
|
161
52
|
|
162
|
-
|
163
|
-
|
164
|
-
const split = chainId.split(":"); // `starknet:networkName`
|
165
|
-
if (split.length < 2) {
|
166
|
-
return;
|
167
|
-
}
|
168
|
-
|
169
|
-
const networkName = split[1] as constants.NetworkName;
|
170
|
-
if (!networkName) {
|
171
|
-
return;
|
172
|
-
}
|
173
|
-
|
174
|
-
const namingContractAddress = networkToNamingContractAddress[networkName];
|
175
|
-
if (!namingContractAddress) {
|
176
|
-
return;
|
177
|
-
}
|
178
|
-
|
179
|
-
this._starknetID = {
|
180
|
-
networkName,
|
181
|
-
namingContractAddress,
|
182
|
-
};
|
53
|
+
get preferredNameService(): string | undefined {
|
54
|
+
return this._preferredNameService;
|
183
55
|
}
|
184
56
|
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
throw new Error(`${this.chainId} is not starknet chain`);
|
189
|
-
}
|
190
|
-
|
191
|
-
if (!this._starknetID) {
|
192
|
-
throw new Error("Starknet ID is not set");
|
193
|
-
}
|
194
|
-
|
195
|
-
if (!isStarkDomain(username)) {
|
196
|
-
return {
|
197
|
-
isFetching: false,
|
198
|
-
error: new Error("Invalid domain for Starknet ID"),
|
199
|
-
};
|
200
|
-
}
|
201
|
-
|
202
|
-
const key = `${this.chainId}/${username}`;
|
203
|
-
|
204
|
-
if (!this._starknetIDFetchDataMap.has(key)) {
|
205
|
-
runInAction(() => {
|
206
|
-
this._starknetIDFetchDataMap.set(key, {
|
207
|
-
isFetching: true,
|
208
|
-
});
|
209
|
-
});
|
210
|
-
|
211
|
-
const domain = encodeDomain(username).map((v) => v.toString(10));
|
212
|
-
|
213
|
-
simpleFetch<{
|
214
|
-
jsonrpc: "2.0";
|
215
|
-
result?: string[];
|
216
|
-
id: string;
|
217
|
-
error?: {
|
218
|
-
code?: number;
|
219
|
-
message?: string;
|
220
|
-
};
|
221
|
-
}>(modularChainInfo.starknet.rpc, "", {
|
222
|
-
method: "POST",
|
223
|
-
headers: {
|
224
|
-
"content-type": "application/json",
|
225
|
-
},
|
226
|
-
body: JSON.stringify({
|
227
|
-
jsonrpc: "2.0",
|
228
|
-
id: "1",
|
229
|
-
method: "starknet_call",
|
230
|
-
params: [
|
231
|
-
{
|
232
|
-
contract_address: this._starknetID.namingContractAddress,
|
233
|
-
calldata: CallData.toHex({ domain, hint: [] }),
|
234
|
-
entry_point_selector:
|
235
|
-
"0x2e269d930f6d7ab92b15ce8ff9f5e63709391617e3465fff79ba6baf278ce60", // selector.getSelectorFromName("domain_to_address"),
|
236
|
-
},
|
237
|
-
"latest",
|
238
|
-
],
|
239
|
-
}),
|
240
|
-
signal: new AbortController().signal,
|
241
|
-
})
|
242
|
-
.then((resp) => {
|
243
|
-
if (resp.data.error && resp.data.error.message) {
|
244
|
-
throw new StarknetIDIsFetchingError(resp.data.error.message);
|
245
|
-
}
|
246
|
-
|
247
|
-
const data = resp.data.result;
|
248
|
-
if (!data) {
|
249
|
-
throw new StarknetIDIsFetchingError("no address found");
|
250
|
-
}
|
251
|
-
|
252
|
-
const rawHexAddr = data[0];
|
253
|
-
if (rawHexAddr === "0x0") {
|
254
|
-
throw new StarknetIDIsFetchingError("no address found");
|
255
|
-
}
|
256
|
-
|
257
|
-
const addr = "0x" + rawHexAddr.replace("0x", "").padStart(64, "0");
|
258
|
-
|
259
|
-
if (!isStarknetHexAddress(addr)) {
|
260
|
-
throw new StarknetIDIsFetchingError("no address found");
|
261
|
-
}
|
262
|
-
|
263
|
-
runInAction(() => {
|
264
|
-
this._starknetIDFetchDataMap.set(key, {
|
265
|
-
isFetching: false,
|
266
|
-
starknetHexAddress: addr,
|
267
|
-
});
|
268
|
-
});
|
269
|
-
})
|
270
|
-
.catch((error) => {
|
271
|
-
runInAction(() => {
|
272
|
-
this._starknetIDFetchDataMap.set(key, {
|
273
|
-
isFetching: false,
|
274
|
-
error,
|
275
|
-
});
|
276
|
-
});
|
277
|
-
});
|
278
|
-
}
|
279
|
-
|
280
|
-
return this._starknetIDFetchDataMap.get(key) ?? { isFetching: false };
|
57
|
+
@action
|
58
|
+
setPreferredNameService(nameService: string | undefined) {
|
59
|
+
this._preferredNameService = nameService;
|
281
60
|
}
|
282
61
|
|
283
|
-
|
284
|
-
return
|
62
|
+
getNameService(type: string): NameService | undefined {
|
63
|
+
return this.nameServices.find((nameService) => nameService.type === type);
|
285
64
|
}
|
286
65
|
|
287
|
-
|
288
|
-
|
289
|
-
if (this.isStarknetIDEnabled) {
|
290
|
-
const parsed = this.value.trim().split(".");
|
291
|
-
return parsed.length > 1 && parsed[parsed.length - 1] === "stark";
|
292
|
-
}
|
293
|
-
|
294
|
-
return false;
|
66
|
+
getNameServices(): NameService[] {
|
67
|
+
return this.nameServices;
|
295
68
|
}
|
296
69
|
|
297
70
|
@computed
|
298
|
-
get
|
299
|
-
|
300
|
-
|
301
|
-
|
71
|
+
get nameServiceResult(): {
|
72
|
+
type: string;
|
73
|
+
address: string;
|
74
|
+
fullName: string;
|
75
|
+
domain: string;
|
76
|
+
suffix: string;
|
77
|
+
}[] {
|
78
|
+
const result: {
|
79
|
+
type: string;
|
80
|
+
address: string;
|
81
|
+
fullName: string;
|
82
|
+
domain: string;
|
83
|
+
suffix: string;
|
84
|
+
}[] = [];
|
85
|
+
for (const nameService of this.nameServices) {
|
86
|
+
if (
|
87
|
+
this.preferredNameService &&
|
88
|
+
nameService.type !== this.preferredNameService
|
89
|
+
) {
|
90
|
+
continue;
|
91
|
+
}
|
302
92
|
|
303
|
-
|
93
|
+
const r = nameService.result;
|
94
|
+
if (r) {
|
95
|
+
result.push({
|
96
|
+
...r,
|
97
|
+
type: nameService.type,
|
98
|
+
});
|
99
|
+
}
|
100
|
+
}
|
101
|
+
return result;
|
304
102
|
}
|
305
103
|
|
306
|
-
|
307
|
-
|
104
|
+
@action
|
105
|
+
setStarknetID(chainId: string) {
|
106
|
+
const found = this.nameServices.find(
|
107
|
+
(nameService) => nameService.type === "starknet-id"
|
108
|
+
);
|
109
|
+
if (found) {
|
110
|
+
(found as StarknetIdNameService).setStarknetID(chainId);
|
111
|
+
} else {
|
112
|
+
this.nameServices.push(
|
113
|
+
new StarknetIdNameService(this, this.chainGetter, chainId)
|
114
|
+
);
|
115
|
+
}
|
308
116
|
}
|
309
117
|
|
310
118
|
get recipient(): string {
|
119
|
+
if (this.nameServiceResult.length > 0) {
|
120
|
+
const r = this.nameServiceResult[0];
|
121
|
+
return r.address;
|
122
|
+
}
|
123
|
+
|
311
124
|
const rawRecipient = this.value.trim();
|
312
125
|
|
313
126
|
const modularChainInfo = this.modularChainInfo;
|
@@ -315,22 +128,12 @@ export class RecipientConfig
|
|
315
128
|
throw new Error("Chain doesn't support the starknet");
|
316
129
|
}
|
317
130
|
|
318
|
-
if (this.isStarknetIDEnabled && this.isStarknetID) {
|
319
|
-
try {
|
320
|
-
return (
|
321
|
-
this.getStarknetIDFetchData(rawRecipient).starknetHexAddress || ""
|
322
|
-
);
|
323
|
-
} catch {
|
324
|
-
return "";
|
325
|
-
}
|
326
|
-
}
|
327
|
-
|
328
131
|
return rawRecipient;
|
329
132
|
}
|
330
133
|
|
331
134
|
@computed
|
332
135
|
get uiProperties(): UIProperties {
|
333
|
-
|
136
|
+
let rawRecipient = this.value.trim();
|
334
137
|
|
335
138
|
if (!rawRecipient) {
|
336
139
|
return {
|
@@ -338,46 +141,9 @@ export class RecipientConfig
|
|
338
141
|
};
|
339
142
|
}
|
340
143
|
|
341
|
-
if (this.
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
if (fetched.isFetching) {
|
346
|
-
return {
|
347
|
-
loadingState: "loading-block",
|
348
|
-
};
|
349
|
-
}
|
350
|
-
|
351
|
-
if (fetched.error) {
|
352
|
-
if (fetched.error instanceof StarknetIDIsFetchingError) {
|
353
|
-
return {
|
354
|
-
error: new StarknetIDFailedToFetchError(
|
355
|
-
"Failed to fetch the address from Starknet ID"
|
356
|
-
),
|
357
|
-
loadingState: fetched.isFetching ? "loading-block" : undefined,
|
358
|
-
};
|
359
|
-
}
|
360
|
-
|
361
|
-
return {
|
362
|
-
error: fetched.error,
|
363
|
-
};
|
364
|
-
}
|
365
|
-
|
366
|
-
if (!fetched.starknetHexAddress) {
|
367
|
-
return {
|
368
|
-
error: new StarknetIDFailedToFetchError(
|
369
|
-
"Failed to fetch the address from Starknet ID"
|
370
|
-
),
|
371
|
-
loadingState: fetched.isFetching ? "loading-block" : undefined,
|
372
|
-
};
|
373
|
-
}
|
374
|
-
|
375
|
-
return {};
|
376
|
-
} catch (e) {
|
377
|
-
return {
|
378
|
-
error: e,
|
379
|
-
};
|
380
|
-
}
|
144
|
+
if (this.nameServiceResult.length > 0) {
|
145
|
+
const r = this.nameServiceResult[0];
|
146
|
+
rawRecipient = r.address;
|
381
147
|
}
|
382
148
|
|
383
149
|
if (!isStarknetHexAddress(rawRecipient)) {
|
@@ -396,6 +162,10 @@ export class RecipientConfig
|
|
396
162
|
@action
|
397
163
|
setValue(value: string): void {
|
398
164
|
this._value = value;
|
165
|
+
|
166
|
+
for (const nameService of this.nameServices) {
|
167
|
+
nameService.setValue(value);
|
168
|
+
}
|
399
169
|
}
|
400
170
|
}
|
401
171
|
|
package/src/tx/types.ts
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
-
import { ERC20Currency } from "@keplr-wallet/types";
|
1
|
+
import { ERC20Currency, ModularChainInfo } from "@keplr-wallet/types";
|
2
2
|
import { CoinPretty } from "@keplr-wallet/unit";
|
3
|
+
import { NameService } from "./name-service";
|
3
4
|
|
4
5
|
export interface ITxChainSetter {
|
5
6
|
chainId: string;
|
6
7
|
setChain(chainId: string): void;
|
8
|
+
modularChainInfo: ModularChainInfo;
|
7
9
|
}
|
8
10
|
|
9
11
|
export interface UIProperties {
|
@@ -64,11 +66,21 @@ export interface IRecipientConfig extends ITxChainSetter {
|
|
64
66
|
uiProperties: UIProperties;
|
65
67
|
}
|
66
68
|
|
67
|
-
export interface
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
69
|
+
export interface IRecipientConfigWithNameServices extends IRecipientConfig {
|
70
|
+
preferredNameService: string | undefined;
|
71
|
+
setPreferredNameService(nameService: string | undefined): void;
|
72
|
+
getNameService(type: string): NameService | undefined;
|
73
|
+
getNameServices(): NameService[];
|
74
|
+
// address를 반환하는 name service의 결과를 반환한다.
|
75
|
+
// preferredNameService가 설정되어 있지 않으면 모든 name service의 결과를 반환한다.
|
76
|
+
// preferredNameService가 설정되어 있으면 해당 name service의 결과를 반환한다.
|
77
|
+
nameServiceResult: {
|
78
|
+
type: string;
|
79
|
+
address: string;
|
80
|
+
fullName: string;
|
81
|
+
domain: string;
|
82
|
+
suffix: string;
|
83
|
+
}[];
|
72
84
|
}
|
73
85
|
|
74
86
|
export interface IAmountConfig extends ITxChainSetter {
|