@keplr-wallet/hooks-evm 0.13.15-rc.0
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/.eslintrc.json +14 -0
- package/build/index.d.ts +1 -0
- package/build/index.js +18 -0
- package/build/index.js.map +1 -0
- package/build/tx/amount.d.ts +27 -0
- package/build/tx/amount.js +238 -0
- package/build/tx/amount.js.map +1 -0
- package/build/tx/chain.d.ts +10 -0
- package/build/tx/chain.js +37 -0
- package/build/tx/chain.js.map +1 -0
- package/build/tx/errors.d.ts +30 -0
- package/build/tx/errors.js +84 -0
- package/build/tx/errors.js.map +1 -0
- package/build/tx/evm-fee-utils.d.ts +28 -0
- package/build/tx/evm-fee-utils.js +133 -0
- package/build/tx/evm-fee-utils.js.map +1 -0
- package/build/tx/fee.d.ts +61 -0
- package/build/tx/fee.js +523 -0
- package/build/tx/fee.js.map +1 -0
- package/build/tx/gas-simulator.d.ts +89 -0
- package/build/tx/gas-simulator.js +465 -0
- package/build/tx/gas-simulator.js.map +1 -0
- package/build/tx/gas.d.ts +12 -0
- package/build/tx/gas.js +84 -0
- package/build/tx/gas.js.map +1 -0
- package/build/tx/index.d.ts +13 -0
- package/build/tx/index.js +30 -0
- package/build/tx/index.js.map +1 -0
- package/build/tx/internal.d.ts +3 -0
- package/build/tx/internal.js +3 -0
- package/build/tx/internal.js.map +1 -0
- package/build/tx/name-service-ens.d.ts +40 -0
- package/build/tx/name-service-ens.js +189 -0
- package/build/tx/name-service-ens.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 +35 -0
- package/build/tx/recipient.js +131 -0
- package/build/tx/recipient.js.map +1 -0
- package/build/tx/send-tx.d.ts +13 -0
- package/build/tx/send-tx.js +24 -0
- package/build/tx/send-tx.js.map +1 -0
- package/build/tx/sender.d.ts +12 -0
- package/build/tx/sender.js +73 -0
- package/build/tx/sender.js.map +1 -0
- package/build/tx/types.d.ts +102 -0
- package/build/tx/types.js +3 -0
- package/build/tx/types.js.map +1 -0
- package/build/tx/validate.d.ts +11 -0
- package/build/tx/validate.js +37 -0
- package/build/tx/validate.js.map +1 -0
- package/package.json +40 -0
- package/src/index.ts +1 -0
- package/src/tx/amount.ts +273 -0
- package/src/tx/chain.ts +31 -0
- package/src/tx/errors.ts +79 -0
- package/src/tx/evm-fee-utils.ts +217 -0
- package/src/tx/fee.ts +622 -0
- package/src/tx/gas-simulator.ts +567 -0
- package/src/tx/gas.ts +93 -0
- package/src/tx/index.ts +13 -0
- package/src/tx/internal.ts +4 -0
- package/src/tx/name-service-ens.ts +207 -0
- package/src/tx/name-service.ts +39 -0
- package/src/tx/recipient.ts +166 -0
- package/src/tx/send-tx.ts +55 -0
- package/src/tx/sender.ts +82 -0
- package/src/tx/types.ts +153 -0
- package/src/tx/validate.ts +55 -0
- package/tsconfig.check.json +90 -0
- package/tsconfig.json +12 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { action, autorun, makeObservable, observable, runInAction } from "mobx";
|
|
2
|
+
import { ChainGetter } from "@keplr-wallet/stores";
|
|
3
|
+
import { FetchDebounce, NameService } from "./name-service";
|
|
4
|
+
import { JsonRpcProvider } from "@ethersproject/providers";
|
|
5
|
+
import { ITxChainSetter } from "./types";
|
|
6
|
+
|
|
7
|
+
export class ENSNameService implements NameService {
|
|
8
|
+
readonly type = "ens";
|
|
9
|
+
|
|
10
|
+
@observable
|
|
11
|
+
protected _isEnabled: boolean = true;
|
|
12
|
+
|
|
13
|
+
@observable
|
|
14
|
+
protected _isFetching: boolean = false;
|
|
15
|
+
|
|
16
|
+
@observable
|
|
17
|
+
protected _value: string = "";
|
|
18
|
+
|
|
19
|
+
@observable.ref
|
|
20
|
+
protected _result:
|
|
21
|
+
| {
|
|
22
|
+
address: string;
|
|
23
|
+
fullName: string;
|
|
24
|
+
domain: string;
|
|
25
|
+
suffix: string;
|
|
26
|
+
}
|
|
27
|
+
| undefined = undefined;
|
|
28
|
+
|
|
29
|
+
// Deep equal check is required to avoid infinite re-render.
|
|
30
|
+
@observable.struct
|
|
31
|
+
protected _ens:
|
|
32
|
+
| {
|
|
33
|
+
chainId: string;
|
|
34
|
+
}
|
|
35
|
+
| undefined = undefined;
|
|
36
|
+
|
|
37
|
+
protected debounce = new FetchDebounce();
|
|
38
|
+
|
|
39
|
+
constructor(
|
|
40
|
+
protected readonly base: ITxChainSetter,
|
|
41
|
+
protected readonly chainGetter: ChainGetter,
|
|
42
|
+
ens:
|
|
43
|
+
| {
|
|
44
|
+
chainId: string;
|
|
45
|
+
}
|
|
46
|
+
| undefined = undefined
|
|
47
|
+
) {
|
|
48
|
+
this._ens = ens;
|
|
49
|
+
|
|
50
|
+
makeObservable(this);
|
|
51
|
+
|
|
52
|
+
autorun(() => {
|
|
53
|
+
noop(this.base.modularChainInfo, this._ens, this.isEnabled, this.value);
|
|
54
|
+
this.fetch();
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@action
|
|
59
|
+
setENS(ens: { chainId: string }) {
|
|
60
|
+
this._ens = ens;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@action
|
|
64
|
+
setIsEnabled(isEnabled: boolean) {
|
|
65
|
+
this._isEnabled = isEnabled;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
get isEnabled(): boolean {
|
|
69
|
+
if (!this._ens) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const u = this.base.modularChainInfo.unwrapped;
|
|
74
|
+
if (u.type !== "evm" && u.type !== "ethermint") {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return this._isEnabled;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@action
|
|
82
|
+
setValue(value: string) {
|
|
83
|
+
this._value = value;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
get value(): string {
|
|
87
|
+
let v = this._value;
|
|
88
|
+
if (this.isEnabled) {
|
|
89
|
+
const suffix = "eth";
|
|
90
|
+
const i = v.lastIndexOf(".");
|
|
91
|
+
if (i >= 0) {
|
|
92
|
+
const tld = v.slice(i + 1);
|
|
93
|
+
if (suffix.startsWith(tld)) {
|
|
94
|
+
v = v.slice(0, i);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return v;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
get result() {
|
|
103
|
+
if (!this.isEnabled) {
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (!this._result) {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (this._result.domain !== this.value) {
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return this._result;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
get isFetching(): boolean {
|
|
119
|
+
return this._isFetching;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
protected async fetch(): Promise<void> {
|
|
123
|
+
if (
|
|
124
|
+
!this.isEnabled ||
|
|
125
|
+
this.value.trim().length === 0 ||
|
|
126
|
+
!this._ens ||
|
|
127
|
+
this.value.length > 20
|
|
128
|
+
) {
|
|
129
|
+
runInAction(() => {
|
|
130
|
+
this._result = undefined;
|
|
131
|
+
this._isFetching = false;
|
|
132
|
+
});
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
this.debounce.run(() => this.fetchInternal());
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
protected async fetchInternal(): Promise<void> {
|
|
140
|
+
const prevValue = this.value;
|
|
141
|
+
try {
|
|
142
|
+
if (!this._ens) {
|
|
143
|
+
throw new Error("ENS is not set");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
runInAction(() => {
|
|
147
|
+
this._isFetching = true;
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
if (!this.chainGetter.hasModularChain(this._ens.chainId)) {
|
|
151
|
+
throw new Error(`Can't find chain: ${this._ens.chainId}`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const ensU = this.chainGetter.getModularChain(
|
|
155
|
+
this._ens.chainId
|
|
156
|
+
).unwrapped;
|
|
157
|
+
if (ensU.type !== "evm" && ensU.type !== "ethermint") {
|
|
158
|
+
throw new Error("ENS chain must be an EVM chain");
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const suffix = "eth";
|
|
162
|
+
const domain = this.value;
|
|
163
|
+
const username = domain + "." + suffix;
|
|
164
|
+
|
|
165
|
+
const resolver = await new JsonRpcProvider(ensU.evm.rpc).getResolver(
|
|
166
|
+
username
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
if (!resolver) {
|
|
170
|
+
throw new Error("Can't find resolver");
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const res = await resolver.getAddress(60);
|
|
174
|
+
|
|
175
|
+
if (this.value === prevValue) {
|
|
176
|
+
if (res) {
|
|
177
|
+
runInAction(() => {
|
|
178
|
+
this._result = {
|
|
179
|
+
address: res,
|
|
180
|
+
fullName: username,
|
|
181
|
+
domain,
|
|
182
|
+
suffix,
|
|
183
|
+
};
|
|
184
|
+
this._isFetching = false;
|
|
185
|
+
});
|
|
186
|
+
} else {
|
|
187
|
+
runInAction(() => {
|
|
188
|
+
this._result = undefined;
|
|
189
|
+
this._isFetching = false;
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
} catch (e) {
|
|
194
|
+
console.log(e);
|
|
195
|
+
if (this.value === prevValue) {
|
|
196
|
+
runInAction(() => {
|
|
197
|
+
this._result = undefined;
|
|
198
|
+
this._isFetching = false;
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const noop = (..._args: any[]) => {
|
|
206
|
+
// noop
|
|
207
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export interface NameService {
|
|
2
|
+
type: string;
|
|
3
|
+
|
|
4
|
+
setIsEnabled: (isEnabled: boolean) => void;
|
|
5
|
+
// must be observable
|
|
6
|
+
isEnabled: boolean;
|
|
7
|
+
|
|
8
|
+
// must be observable
|
|
9
|
+
result:
|
|
10
|
+
| {
|
|
11
|
+
address: string;
|
|
12
|
+
fullName: string;
|
|
13
|
+
domain: string;
|
|
14
|
+
suffix: string;
|
|
15
|
+
}
|
|
16
|
+
| undefined;
|
|
17
|
+
|
|
18
|
+
// must be observable
|
|
19
|
+
isFetching: boolean;
|
|
20
|
+
|
|
21
|
+
setValue: (value: string) => void;
|
|
22
|
+
value: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class FetchDebounce {
|
|
26
|
+
readonly debounceMs = 50;
|
|
27
|
+
|
|
28
|
+
protected timeout: NodeJS.Timeout | undefined = undefined;
|
|
29
|
+
|
|
30
|
+
run(fn: () => Promise<void>) {
|
|
31
|
+
if (this.timeout) {
|
|
32
|
+
clearTimeout(this.timeout);
|
|
33
|
+
}
|
|
34
|
+
this.timeout = setTimeout(() => {
|
|
35
|
+
this.timeout = undefined;
|
|
36
|
+
fn();
|
|
37
|
+
}, this.debounceMs);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IRecipientConfig,
|
|
3
|
+
UIProperties,
|
|
4
|
+
IRecipientConfigWithNameServices,
|
|
5
|
+
} from "./types";
|
|
6
|
+
import { TxChainSetter } from "./chain";
|
|
7
|
+
import { ChainGetter } from "@keplr-wallet/stores";
|
|
8
|
+
import { EthereumAccountBase } from "@keplr-wallet/stores-eth";
|
|
9
|
+
import { action, computed, makeObservable, observable } from "mobx";
|
|
10
|
+
import { EmptyAddressError, InvalidHexError } from "./errors";
|
|
11
|
+
import { useState } from "react";
|
|
12
|
+
import { NameService } from "./name-service";
|
|
13
|
+
import { ENSNameService } from "./name-service-ens";
|
|
14
|
+
|
|
15
|
+
export class RecipientConfig
|
|
16
|
+
extends TxChainSetter
|
|
17
|
+
implements IRecipientConfig, IRecipientConfigWithNameServices
|
|
18
|
+
{
|
|
19
|
+
@observable
|
|
20
|
+
protected _value: string = "";
|
|
21
|
+
|
|
22
|
+
@observable
|
|
23
|
+
protected _preferredNameService: string | undefined = undefined;
|
|
24
|
+
|
|
25
|
+
@observable.ref
|
|
26
|
+
protected nameServices: NameService[] = [];
|
|
27
|
+
|
|
28
|
+
constructor(
|
|
29
|
+
chainGetter: ChainGetter,
|
|
30
|
+
initialChainId: string,
|
|
31
|
+
ens?: { chainId: string }
|
|
32
|
+
) {
|
|
33
|
+
super(chainGetter, initialChainId);
|
|
34
|
+
|
|
35
|
+
if (ens) {
|
|
36
|
+
this.nameServices.push(new ENSNameService(this, chainGetter, ens));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
makeObservable(this);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get preferredNameService(): string | undefined {
|
|
43
|
+
return this._preferredNameService;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@action
|
|
47
|
+
setPreferredNameService(nameService: string | undefined) {
|
|
48
|
+
this._preferredNameService = nameService;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
getNameService(type: string): NameService | undefined {
|
|
52
|
+
return this.nameServices.find((nameService) => nameService.type === type);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
getNameServices(): NameService[] {
|
|
56
|
+
return this.nameServices;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@computed
|
|
60
|
+
get nameServiceResult(): {
|
|
61
|
+
type: string;
|
|
62
|
+
address: string;
|
|
63
|
+
fullName: string;
|
|
64
|
+
domain: string;
|
|
65
|
+
suffix: string;
|
|
66
|
+
}[] {
|
|
67
|
+
const result: {
|
|
68
|
+
type: string;
|
|
69
|
+
address: string;
|
|
70
|
+
fullName: string;
|
|
71
|
+
domain: string;
|
|
72
|
+
suffix: string;
|
|
73
|
+
}[] = [];
|
|
74
|
+
for (const nameService of this.nameServices) {
|
|
75
|
+
if (
|
|
76
|
+
this.preferredNameService &&
|
|
77
|
+
nameService.type !== this.preferredNameService
|
|
78
|
+
) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const r = nameService.result;
|
|
83
|
+
if (r) {
|
|
84
|
+
result.push({
|
|
85
|
+
...r,
|
|
86
|
+
type: nameService.type,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
@action
|
|
94
|
+
setENS(ens: { chainId: string }) {
|
|
95
|
+
const found = this.nameServices.find(
|
|
96
|
+
(nameService) => nameService.type === "ens"
|
|
97
|
+
);
|
|
98
|
+
if (found) {
|
|
99
|
+
(found as ENSNameService).setENS(ens);
|
|
100
|
+
} else {
|
|
101
|
+
this.nameServices.push(new ENSNameService(this, this.chainGetter, ens));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
get recipient(): string {
|
|
106
|
+
if (this.nameServiceResult.length > 0) {
|
|
107
|
+
const r = this.nameServiceResult[0];
|
|
108
|
+
return r.address;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return this.value.trim();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@computed
|
|
115
|
+
get uiProperties(): UIProperties {
|
|
116
|
+
let rawRecipient = this.value.trim();
|
|
117
|
+
|
|
118
|
+
if (!rawRecipient) {
|
|
119
|
+
return {
|
|
120
|
+
error: new EmptyAddressError("Address is empty"),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (this.nameServiceResult.length > 0) {
|
|
125
|
+
const r = this.nameServiceResult[0];
|
|
126
|
+
rawRecipient = r.address;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (!EthereumAccountBase.isEthereumHexAddressWithChecksum(rawRecipient)) {
|
|
130
|
+
return {
|
|
131
|
+
error: new InvalidHexError("Invalid hex address"),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return {};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
get value(): string {
|
|
139
|
+
return this._value;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
@action
|
|
143
|
+
setValue(value: string): void {
|
|
144
|
+
this._value = value;
|
|
145
|
+
|
|
146
|
+
for (const nameService of this.nameServices) {
|
|
147
|
+
nameService.setValue(value);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export const useRecipientConfig = (
|
|
153
|
+
chainGetter: ChainGetter,
|
|
154
|
+
chainId: string,
|
|
155
|
+
options?: { ens?: { chainId: string } }
|
|
156
|
+
) => {
|
|
157
|
+
const [config] = useState(
|
|
158
|
+
() => new RecipientConfig(chainGetter, chainId, options?.ens)
|
|
159
|
+
);
|
|
160
|
+
config.setChain(chainId);
|
|
161
|
+
if (options?.ens) {
|
|
162
|
+
config.setENS(options.ens);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return config;
|
|
166
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ChainGetter } from "@keplr-wallet/stores";
|
|
2
|
+
import {
|
|
3
|
+
useFeeConfig,
|
|
4
|
+
useGasConfig,
|
|
5
|
+
useRecipientConfig,
|
|
6
|
+
useSenderConfig,
|
|
7
|
+
} from "./index";
|
|
8
|
+
import { useAmountConfig } from "./amount";
|
|
9
|
+
import { QueriesStore } from "./internal";
|
|
10
|
+
|
|
11
|
+
export const useSendTxConfig = (
|
|
12
|
+
chainGetter: ChainGetter,
|
|
13
|
+
queriesStore: QueriesStore,
|
|
14
|
+
chainId: string,
|
|
15
|
+
sender: string,
|
|
16
|
+
initialGas: number,
|
|
17
|
+
options?: {
|
|
18
|
+
ens?: {
|
|
19
|
+
chainId: string;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
) => {
|
|
23
|
+
const senderConfig = useSenderConfig(chainGetter, chainId, sender);
|
|
24
|
+
|
|
25
|
+
const amountConfig = useAmountConfig(
|
|
26
|
+
chainGetter,
|
|
27
|
+
queriesStore,
|
|
28
|
+
chainId,
|
|
29
|
+
senderConfig
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const gasConfig = useGasConfig(chainGetter, chainId, initialGas);
|
|
33
|
+
const feeConfig = useFeeConfig(
|
|
34
|
+
chainGetter,
|
|
35
|
+
queriesStore,
|
|
36
|
+
chainId,
|
|
37
|
+
senderConfig,
|
|
38
|
+
amountConfig,
|
|
39
|
+
gasConfig
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
amountConfig.setFeeConfig(feeConfig);
|
|
43
|
+
|
|
44
|
+
const recipientConfig = useRecipientConfig(chainGetter, chainId, {
|
|
45
|
+
ens: options?.ens,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
senderConfig,
|
|
50
|
+
amountConfig,
|
|
51
|
+
gasConfig,
|
|
52
|
+
feeConfig,
|
|
53
|
+
recipientConfig,
|
|
54
|
+
};
|
|
55
|
+
};
|
package/src/tx/sender.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { ISenderConfig, UIProperties } from "./types";
|
|
2
|
+
import { TxChainSetter } from "./chain";
|
|
3
|
+
import { action, makeObservable, observable } from "mobx";
|
|
4
|
+
import { ChainGetter } from "@keplr-wallet/stores";
|
|
5
|
+
import { useState } from "react";
|
|
6
|
+
import { EmptyAddressError, InvalidHexError } from "./errors";
|
|
7
|
+
import { Buffer } from "buffer";
|
|
8
|
+
|
|
9
|
+
export class SenderConfig extends TxChainSetter implements ISenderConfig {
|
|
10
|
+
@observable
|
|
11
|
+
protected _value: string = "";
|
|
12
|
+
|
|
13
|
+
constructor(
|
|
14
|
+
chainGetter: ChainGetter,
|
|
15
|
+
initialChainId: string,
|
|
16
|
+
initialSender: string
|
|
17
|
+
) {
|
|
18
|
+
super(chainGetter, initialChainId);
|
|
19
|
+
|
|
20
|
+
this._value = initialSender;
|
|
21
|
+
|
|
22
|
+
makeObservable(this);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
get sender(): string {
|
|
26
|
+
return this._value;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get value(): string {
|
|
30
|
+
return this._value;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@action
|
|
34
|
+
setValue(value: string): void {
|
|
35
|
+
this._value = value;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get uiProperties(): UIProperties {
|
|
39
|
+
if (!this.value) {
|
|
40
|
+
return {
|
|
41
|
+
error: new EmptyAddressError("Address is empty"),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!this.value.startsWith("0x")) {
|
|
46
|
+
return {
|
|
47
|
+
error: new InvalidHexError("Invalid hex address for chain"),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
{
|
|
52
|
+
const hex = this.value.replace("0x", "");
|
|
53
|
+
const buf = Buffer.from(hex, "hex");
|
|
54
|
+
if (buf.length !== 20) {
|
|
55
|
+
return {
|
|
56
|
+
error: new InvalidHexError("Invalid hex address for chain"),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (hex.toLowerCase() !== buf.toString("hex").toLowerCase()) {
|
|
60
|
+
return {
|
|
61
|
+
error: new InvalidHexError("Invalid hex address for chain"),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return {};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const useSenderConfig = (
|
|
71
|
+
chainGetter: ChainGetter,
|
|
72
|
+
chainId: string,
|
|
73
|
+
sender: string
|
|
74
|
+
) => {
|
|
75
|
+
const [config] = useState(
|
|
76
|
+
() => new SenderConfig(chainGetter, chainId, sender)
|
|
77
|
+
);
|
|
78
|
+
config.setChain(chainId);
|
|
79
|
+
config.setValue(sender);
|
|
80
|
+
|
|
81
|
+
return config;
|
|
82
|
+
};
|
package/src/tx/types.ts
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AppCurrency,
|
|
3
|
+
EvmGasSimulationOutcome,
|
|
4
|
+
FeeCurrency,
|
|
5
|
+
StdFee,
|
|
6
|
+
} from "@keplr-wallet/types";
|
|
7
|
+
import { CoinPretty, Dec } from "@keplr-wallet/unit";
|
|
8
|
+
import { NameService } from "./name-service";
|
|
9
|
+
import { IModularChainInfoImpl } from "@keplr-wallet/stores";
|
|
10
|
+
|
|
11
|
+
export interface ITxChainSetter {
|
|
12
|
+
chainId: string;
|
|
13
|
+
setChain(chainId: string): void;
|
|
14
|
+
modularChainInfo: IModularChainInfoImpl;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface UIProperties {
|
|
18
|
+
// There is an error that cannot proceed the tx.
|
|
19
|
+
readonly error?: Error;
|
|
20
|
+
// Able to handle tx but prefer to show warning
|
|
21
|
+
readonly warning?: Error;
|
|
22
|
+
// Prefer that the loading UI is displayed.
|
|
23
|
+
// In the case of "loading-block", the UI should handle it so that the user cannot proceed until loading is completed.
|
|
24
|
+
readonly loadingState?: "loading" | "loading-block";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type FeeType = "high" | "average" | "low";
|
|
28
|
+
export type EVMFeeType = FeeType | "custom";
|
|
29
|
+
|
|
30
|
+
export interface IGasConfig extends ITxChainSetter {
|
|
31
|
+
value: string;
|
|
32
|
+
setValue(value: string | number): void;
|
|
33
|
+
|
|
34
|
+
gas: number;
|
|
35
|
+
|
|
36
|
+
uiProperties: UIProperties;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ISenderConfig extends ITxChainSetter {
|
|
40
|
+
value: string;
|
|
41
|
+
setValue(value: string): void;
|
|
42
|
+
|
|
43
|
+
sender: string;
|
|
44
|
+
|
|
45
|
+
uiProperties: UIProperties;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface IFeeConfig extends ITxChainSetter {
|
|
49
|
+
type: EVMFeeType;
|
|
50
|
+
setType(type: EVMFeeType): void;
|
|
51
|
+
|
|
52
|
+
maxFeePerGas: Dec | undefined;
|
|
53
|
+
maxPriorityFeePerGas: Dec | undefined;
|
|
54
|
+
gasPrice: Dec | undefined;
|
|
55
|
+
|
|
56
|
+
fee: CoinPretty | undefined;
|
|
57
|
+
maxFee: CoinPretty | undefined;
|
|
58
|
+
|
|
59
|
+
l1DataFee: Dec | undefined;
|
|
60
|
+
setL1DataFee(fee: Dec): void;
|
|
61
|
+
|
|
62
|
+
refreshEIP1559TxFees(): void;
|
|
63
|
+
|
|
64
|
+
customPriorityFee: string;
|
|
65
|
+
customPriorityFeeInput: string;
|
|
66
|
+
setCustomPriorityFee(fee: string): void;
|
|
67
|
+
|
|
68
|
+
isLegacyFeeMode: boolean;
|
|
69
|
+
customGasPrice: string;
|
|
70
|
+
customGasPriceInput: string;
|
|
71
|
+
setCustomGasPrice(value: string): void;
|
|
72
|
+
|
|
73
|
+
getEIP1559TxFees(feeType: EVMFeeType | "manual"): {
|
|
74
|
+
maxPriorityFeePerGas?: Dec;
|
|
75
|
+
maxFeePerGas?: Dec;
|
|
76
|
+
gasPrice?: Dec;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// hooks IFeeConfig 구조 호환용
|
|
80
|
+
fees: CoinPretty[];
|
|
81
|
+
selectableFeeCurrencies: FeeCurrency[];
|
|
82
|
+
setFee(
|
|
83
|
+
fee:
|
|
84
|
+
| { type: EVMFeeType; currency: FeeCurrency }
|
|
85
|
+
| CoinPretty
|
|
86
|
+
| CoinPretty[]
|
|
87
|
+
| undefined
|
|
88
|
+
): void;
|
|
89
|
+
getFeeTypePrettyForFeeCurrency(
|
|
90
|
+
currency: FeeCurrency,
|
|
91
|
+
feeType: EVMFeeType
|
|
92
|
+
): CoinPretty;
|
|
93
|
+
toStdFee(): StdFee;
|
|
94
|
+
|
|
95
|
+
uiProperties: UIProperties;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface IRecipientConfig extends ITxChainSetter {
|
|
99
|
+
value: string;
|
|
100
|
+
setValue(value: string): void;
|
|
101
|
+
|
|
102
|
+
recipient: string;
|
|
103
|
+
|
|
104
|
+
uiProperties: UIProperties;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface IRecipientConfigWithNameServices extends IRecipientConfig {
|
|
108
|
+
preferredNameService: string | undefined;
|
|
109
|
+
setPreferredNameService(nameService: string | undefined): void;
|
|
110
|
+
getNameService(type: string): NameService | undefined;
|
|
111
|
+
getNameServices(): NameService[];
|
|
112
|
+
nameServiceResult: {
|
|
113
|
+
type: string;
|
|
114
|
+
address: string;
|
|
115
|
+
fullName: string;
|
|
116
|
+
domain: string;
|
|
117
|
+
suffix: string;
|
|
118
|
+
}[];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export interface IAmountConfig extends ITxChainSetter {
|
|
122
|
+
amount: CoinPretty[];
|
|
123
|
+
|
|
124
|
+
value: string;
|
|
125
|
+
setValue(value: string): void;
|
|
126
|
+
|
|
127
|
+
currency: AppCurrency;
|
|
128
|
+
setCurrency(currency: AppCurrency | undefined): void;
|
|
129
|
+
canUseCurrency(currency: AppCurrency): boolean;
|
|
130
|
+
|
|
131
|
+
// Zero means unset.
|
|
132
|
+
fraction: number;
|
|
133
|
+
setFraction(fraction: number): void;
|
|
134
|
+
|
|
135
|
+
uiProperties: UIProperties;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export interface IGasSimulator {
|
|
139
|
+
enabled: boolean;
|
|
140
|
+
setEnabled(value: boolean): void;
|
|
141
|
+
|
|
142
|
+
isSimulating: boolean;
|
|
143
|
+
|
|
144
|
+
gasEstimated: number | undefined;
|
|
145
|
+
gasAdjustment: number;
|
|
146
|
+
|
|
147
|
+
gasAdjustmentValue: string;
|
|
148
|
+
setGasAdjustmentValue(gasAdjustment: string | number): void;
|
|
149
|
+
|
|
150
|
+
evmSimulationOutcome?: EvmGasSimulationOutcome;
|
|
151
|
+
|
|
152
|
+
uiProperties: UIProperties;
|
|
153
|
+
}
|