@instadapp/avocado-base 0.0.0-dev.f5f443b → 0.0.0-dev.f6bd350
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/.github/workflows/npm-publish-dev.yml +2 -5
- package/.vscode/settings.json +67 -0
- package/abi/avoFactoryProxy.json +1 -1
- package/abi/forwarder.json +9 -9
- package/abi/multisigAgnosticForwarder.json +937 -0
- package/abi/multisigForwarder.json +697 -0
- package/app.vue +9 -2
- package/assets/images/icons/arrow-left.svg +5 -0
- package/assets/images/icons/arrow-right.svg +5 -0
- package/assets/images/icons/avocado.svg +4 -0
- package/assets/images/icons/bridge-2.svg +3 -0
- package/assets/images/icons/bridge.svg +7 -0
- package/assets/images/icons/calendar.svg +8 -0
- package/assets/images/icons/change-threshold.svg +4 -0
- package/assets/images/icons/check-circle.svg +4 -0
- package/assets/images/icons/check.svg +3 -0
- package/assets/images/icons/chevron-down.svg +4 -0
- package/assets/images/icons/clipboard.svg +7 -0
- package/assets/images/icons/clock-circle.svg +5 -0
- package/assets/images/icons/copy.svg +12 -0
- package/assets/images/icons/cross-transfer.svg +7 -0
- package/assets/images/icons/dapp.svg +4 -0
- package/assets/images/icons/deploy.svg +12 -0
- package/assets/images/icons/error-circle.svg +6 -0
- package/assets/images/icons/exclamation-circle.svg +13 -0
- package/assets/images/icons/exclamation-octagon.svg +13 -0
- package/assets/images/icons/exclamation-triangle.svg +5 -0
- package/assets/images/icons/external-link.svg +6 -0
- package/assets/images/icons/eye.svg +4 -0
- package/assets/images/icons/flowers.svg +8 -0
- package/assets/images/icons/gas-emoji.svg +193 -0
- package/assets/images/icons/gas.svg +14 -0
- package/assets/images/icons/gift.svg +153 -0
- package/assets/images/icons/globe.svg +110 -0
- package/assets/images/icons/hamburger.svg +6 -0
- package/assets/images/icons/hammer.svg +5 -0
- package/assets/images/icons/info-2.svg +12 -0
- package/assets/images/icons/instadapp-pro.svg +4 -0
- package/assets/images/icons/logout.svg +3 -0
- package/assets/images/icons/moon.svg +3 -0
- package/assets/images/icons/multi-send.svg +7 -0
- package/assets/images/icons/network.svg +13 -0
- package/assets/images/icons/options.svg +5 -0
- package/assets/images/icons/permit-sign.svg +11 -0
- package/assets/images/icons/plus-circle.svg +6 -0
- package/assets/images/icons/plus.svg +5 -0
- package/assets/images/icons/power-off-bg.svg +24 -0
- package/assets/images/icons/power-off.svg +19 -0
- package/assets/images/icons/power-on.svg +19 -0
- package/assets/images/icons/qr.svg +20 -0
- package/assets/images/icons/question-circle.svg +14 -0
- package/assets/images/icons/refresh.svg +6 -0
- package/assets/images/icons/reject-proposal.svg +6 -0
- package/assets/images/icons/search.svg +12 -0
- package/assets/images/icons/stars.svg +4 -0
- package/assets/images/icons/sun.svg +3 -0
- package/assets/images/icons/transfer.svg +5 -0
- package/assets/images/icons/trash-2.svg +8 -0
- package/assets/images/icons/upgrade.svg +4 -0
- package/assets/images/icons/wave.svg +214 -0
- package/assets/images/icons/x.svg +5 -0
- package/components/ActionLogo.vue +42 -0
- package/components/ActionMetadata.vue +88 -0
- package/components/Address.vue +74 -0
- package/components/AuthorityAvatar.vue +39 -0
- package/components/ChainLogo.vue +18 -563
- package/components/CopyClipboard.vue +42 -0
- package/components/metadata/Bridge.vue +58 -0
- package/components/metadata/CrossTransfer.vue +76 -0
- package/components/metadata/GasTopup.vue +38 -0
- package/components/metadata/Permit2.vue +41 -0
- package/components/metadata/Signers.vue +19 -0
- package/components/metadata/Swap.vue +66 -0
- package/components/metadata/Transfer.vue +48 -0
- package/components.d.ts +13 -0
- package/contracts/Forwarder.ts +4 -4
- package/contracts/MultisigAgnosticForwarder.ts +1423 -0
- package/contracts/MultisigForwarder.ts +859 -0
- package/contracts/factories/Forwarder__factory.ts +8 -8
- package/contracts/factories/MultisigAgnosticForwarder__factory.ts +2135 -0
- package/contracts/factories/MultisigForwarder__factory.ts +721 -0
- package/contracts/factories/index.ts +2 -0
- package/contracts/index.ts +4 -0
- package/eslint.config.mjs +34 -0
- package/nuxt.config.ts +27 -2
- package/package.json +17 -12
- package/server/utils/index.ts +4 -4
- package/utils/avocado.ts +18 -16
- package/utils/bignumber.ts +60 -29
- package/utils/formatter.ts +55 -61
- package/utils/helper.ts +39 -28
- package/utils/metadata.ts +569 -321
- package/utils/network.ts +552 -184
- package/utils/services.ts +19 -0
- package/utils/utils.d.ts +146 -116
package/utils/metadata.ts
CHANGED
|
@@ -1,178 +1,265 @@
|
|
|
1
|
-
import { ethers, utils } from
|
|
2
|
-
import { Forwarder__factory } from
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
1
|
+
import { ethers, utils } from 'ethers'
|
|
2
|
+
import { Forwarder__factory, MultisigAgnosticForwarder__factory, MultisigForwarder__factory } from '../contracts'
|
|
3
|
+
import { toBN } from './bignumber'
|
|
4
|
+
|
|
5
|
+
export const MetadataEnums = {
|
|
6
|
+
'transfer': 'transfer',
|
|
7
|
+
'bridge': 'bridge',
|
|
8
|
+
'bridge-v2': 'bridge-v2',
|
|
9
|
+
'swap': 'swap',
|
|
10
|
+
'gas-topup': 'gas-topup',
|
|
11
|
+
'upgrade': 'upgrade',
|
|
12
|
+
'dapp': 'dapp',
|
|
13
|
+
'deploy': 'deploy',
|
|
14
|
+
'permit2': 'permit2',
|
|
15
|
+
'cross-transfer': 'cross-transfer',
|
|
16
|
+
'cross-transfer-v2': 'cross-transfer-v2',
|
|
17
|
+
'auth': 'auth',
|
|
18
|
+
'rejection': 'rejection',
|
|
19
|
+
'instadapp-pro': 'instadapp-pro',
|
|
20
|
+
'add-signers': 'add-signers',
|
|
21
|
+
'remove-signers': 'remove-signers',
|
|
22
|
+
'change-threshold': 'change-threshold',
|
|
23
|
+
'import': 'import',
|
|
24
|
+
'mass': 'mass',
|
|
25
|
+
'tx-builder': 'tx-builder',
|
|
26
|
+
'avocado-bridge': 'avocado-bridge',
|
|
27
|
+
} as const
|
|
28
|
+
|
|
29
|
+
const multiMetadataTypes = ['bytes[]']
|
|
30
|
+
|
|
31
|
+
const metadataTypes = ['bytes32 type', 'uint8 version', 'bytes data']
|
|
32
|
+
|
|
33
|
+
const actionMetadataTypes: Record<MetadataTypes, string[]> = {
|
|
34
|
+
'transfer': ['address token', 'uint256 amount', 'address receiver'],
|
|
35
|
+
'cross-transfer': [
|
|
36
|
+
'address fromToken',
|
|
37
|
+
'address toToken',
|
|
38
|
+
'uint256 toChainId',
|
|
39
|
+
'uint256 amount',
|
|
40
|
+
'address receiver',
|
|
16
41
|
],
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"address nativeToken",
|
|
42
|
+
'cross-transfer-v2': [
|
|
43
|
+
'address fromToken',
|
|
44
|
+
'address toToken',
|
|
45
|
+
'uint256 toChainId',
|
|
46
|
+
'uint256 amount',
|
|
47
|
+
'address receiver',
|
|
48
|
+
'bytes32 provider',
|
|
25
49
|
],
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
50
|
+
'bridge': [
|
|
51
|
+
'uint256 amount',
|
|
52
|
+
'address receiver',
|
|
53
|
+
'address fromToken',
|
|
54
|
+
'address toToken',
|
|
55
|
+
'uint256 toChainId',
|
|
56
|
+
'uint256 bridgeFee',
|
|
57
|
+
'address nativeToken',
|
|
33
58
|
],
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"uint48 expiration",
|
|
59
|
+
'bridge-v2': [
|
|
60
|
+
'uint256 amount',
|
|
61
|
+
'address receiver',
|
|
62
|
+
'address fromToken',
|
|
63
|
+
'address toToken',
|
|
64
|
+
'uint256 toChainId',
|
|
65
|
+
'uint256 bridgeFee',
|
|
66
|
+
'address nativeToken',
|
|
67
|
+
'bytes32 provider',
|
|
44
68
|
],
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
69
|
+
'swap': [
|
|
70
|
+
'address sellToken',
|
|
71
|
+
'address buyToken',
|
|
72
|
+
'uint256 sellAmount',
|
|
73
|
+
'uint256 buyAmount',
|
|
74
|
+
'address receiver',
|
|
75
|
+
'bytes32 protocol',
|
|
76
|
+
],
|
|
77
|
+
'gas-topup': ['uint256 amount', 'address token', 'address onBehalf'],
|
|
78
|
+
'upgrade': ['bytes32 version', 'address walletImpl'],
|
|
79
|
+
'dapp': ['string name', 'string url'],
|
|
80
|
+
'import': ['bytes32 protocol', 'uint256 valueInUsd'],
|
|
81
|
+
'auth': ['address address', 'uint256 chainId', 'bool remove'],
|
|
82
|
+
'deploy': [],
|
|
83
|
+
'tx-builder': ['bytes32 actionCount'],
|
|
84
|
+
'permit2': [
|
|
85
|
+
'address token',
|
|
86
|
+
'address spender',
|
|
87
|
+
'uint160 amount',
|
|
88
|
+
'uint48 expiration',
|
|
89
|
+
],
|
|
90
|
+
'instadapp-pro': ['string castDetails'],
|
|
91
|
+
'add-signers': ['address[] signers'],
|
|
92
|
+
'remove-signers': ['address[] signers'],
|
|
93
|
+
'change-threshold': ['uint8 count'],
|
|
94
|
+
'rejection': ['bytes32 id'],
|
|
95
|
+
'avocado-bridge': ['bytes32 id', 'uint256 toChainId'],
|
|
96
|
+
'mass': ['bool isMass'],
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function encodeMetadata(props: MetadataProps) {
|
|
48
100
|
return ethers.utils.defaultAbiCoder.encode(metadataTypes, [
|
|
49
101
|
ethers.utils.formatBytes32String(props.type),
|
|
50
|
-
props.version ||
|
|
102
|
+
props.version || '1',
|
|
51
103
|
props.encodedData,
|
|
52
|
-
])
|
|
53
|
-
}
|
|
104
|
+
])
|
|
105
|
+
}
|
|
54
106
|
|
|
55
|
-
export
|
|
56
|
-
params: DappMetadataProps,
|
|
57
|
-
single = true
|
|
58
|
-
) => {
|
|
107
|
+
export function encodeDappMetadata(params: DappMetadataProps, single = true) {
|
|
59
108
|
const encodedData = ethers.utils.defaultAbiCoder.encode(
|
|
60
109
|
actionMetadataTypes.dapp,
|
|
61
|
-
[params.name, params.url]
|
|
62
|
-
)
|
|
110
|
+
[params.name, params.url],
|
|
111
|
+
)
|
|
63
112
|
|
|
64
113
|
const data = encodeMetadata({
|
|
65
|
-
type:
|
|
114
|
+
type: MetadataEnums.dapp,
|
|
66
115
|
encodedData,
|
|
67
|
-
})
|
|
116
|
+
})
|
|
68
117
|
|
|
69
|
-
return single ? encodeMultipleActions(data) : data
|
|
70
|
-
}
|
|
118
|
+
return single ? encodeMultipleActions(data) : data
|
|
119
|
+
}
|
|
71
120
|
|
|
72
|
-
export
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
121
|
+
export function encodeAvocadoBridgeMetadata(id: string, toChainId: string | number, single = true) {
|
|
122
|
+
const encodedData = ethers.utils.defaultAbiCoder.encode(
|
|
123
|
+
actionMetadataTypes['avocado-bridge'],
|
|
124
|
+
[id, toChainId],
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
const data = encodeMetadata({
|
|
128
|
+
type: MetadataEnums['avocado-bridge'],
|
|
129
|
+
encodedData,
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
return single ? encodeMultipleActions(data) : data
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function encodeTransferMetadata(params: SendMetadataProps, single = true) {
|
|
76
136
|
const encodedData = ethers.utils.defaultAbiCoder.encode(
|
|
77
137
|
actionMetadataTypes.transfer,
|
|
78
|
-
[params.token, params.amount, params.receiver]
|
|
79
|
-
)
|
|
138
|
+
[params.token, params.amount, params.receiver],
|
|
139
|
+
)
|
|
80
140
|
|
|
81
141
|
const data = encodeMetadata({
|
|
82
|
-
type:
|
|
142
|
+
type: MetadataEnums.transfer,
|
|
83
143
|
encodedData,
|
|
84
|
-
})
|
|
144
|
+
})
|
|
85
145
|
|
|
86
|
-
return single ? encodeMultipleActions(data) : data
|
|
87
|
-
}
|
|
146
|
+
return single ? encodeMultipleActions(data) : data
|
|
147
|
+
}
|
|
88
148
|
|
|
89
|
-
export
|
|
90
|
-
params: CrossSendMetadataProps,
|
|
91
|
-
single = true
|
|
92
|
-
) => {
|
|
149
|
+
export function encodeRejectionMetadata(id: string, single = true) {
|
|
93
150
|
const encodedData = ethers.utils.defaultAbiCoder.encode(
|
|
94
|
-
actionMetadataTypes
|
|
95
|
-
[
|
|
96
|
-
|
|
97
|
-
params.toToken,
|
|
98
|
-
params.toChainId,
|
|
99
|
-
params.amount,
|
|
100
|
-
params.receiver,
|
|
101
|
-
]
|
|
102
|
-
);
|
|
151
|
+
actionMetadataTypes.rejection,
|
|
152
|
+
[id],
|
|
153
|
+
)
|
|
103
154
|
|
|
104
155
|
const data = encodeMetadata({
|
|
105
|
-
type:
|
|
156
|
+
type: MetadataEnums.rejection,
|
|
106
157
|
encodedData,
|
|
107
|
-
})
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
return single ? encodeMultipleActions(data) : data
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function encodeCrossTransferMetadata(params: CrossSendMetadataProps, single = true) {
|
|
164
|
+
const type = params.provider ? MetadataEnums['cross-transfer-v2'] : MetadataEnums['cross-transfer']
|
|
165
|
+
|
|
166
|
+
const encodedData = params.provider
|
|
167
|
+
? ethers.utils.defaultAbiCoder.encode(
|
|
168
|
+
actionMetadataTypes[type],
|
|
169
|
+
[
|
|
170
|
+
params.fromToken,
|
|
171
|
+
params.toToken,
|
|
172
|
+
params.toChainId,
|
|
173
|
+
params.amount,
|
|
174
|
+
params.receiver,
|
|
175
|
+
params.provider,
|
|
176
|
+
],
|
|
177
|
+
)
|
|
178
|
+
: ethers.utils.defaultAbiCoder.encode(
|
|
179
|
+
actionMetadataTypes[type],
|
|
180
|
+
[
|
|
181
|
+
params.fromToken,
|
|
182
|
+
params.toToken,
|
|
183
|
+
params.toChainId,
|
|
184
|
+
params.amount,
|
|
185
|
+
params.receiver,
|
|
186
|
+
],
|
|
187
|
+
)
|
|
108
188
|
|
|
109
|
-
|
|
110
|
-
|
|
189
|
+
const data = encodeMetadata({
|
|
190
|
+
type: MetadataEnums[type],
|
|
191
|
+
encodedData,
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
return single ? encodeMultipleActions(data) : data
|
|
195
|
+
}
|
|
111
196
|
|
|
112
|
-
export
|
|
113
|
-
params: AuthMetadataProps,
|
|
114
|
-
single = true
|
|
115
|
-
) => {
|
|
197
|
+
export function encodeAuthMetadata(params: AuthMetadataProps, single = true) {
|
|
116
198
|
const encodedData = ethers.utils.defaultAbiCoder.encode(
|
|
117
|
-
actionMetadataTypes
|
|
118
|
-
[params.address, params.chainId, params.remove]
|
|
119
|
-
)
|
|
199
|
+
actionMetadataTypes.auth,
|
|
200
|
+
[params.address, params.chainId, params.remove],
|
|
201
|
+
)
|
|
120
202
|
|
|
121
203
|
const data = encodeMetadata({
|
|
122
|
-
type:
|
|
204
|
+
type: MetadataEnums.auth,
|
|
123
205
|
encodedData,
|
|
124
|
-
})
|
|
206
|
+
})
|
|
125
207
|
|
|
126
|
-
return single ? encodeMultipleActions(data) : data
|
|
127
|
-
}
|
|
208
|
+
return single ? encodeMultipleActions(data) : data
|
|
209
|
+
}
|
|
128
210
|
|
|
129
|
-
export
|
|
211
|
+
export function encodeDeployMetadata(single = true) {
|
|
130
212
|
const data = encodeMetadata({
|
|
131
|
-
type:
|
|
132
|
-
encodedData:
|
|
133
|
-
})
|
|
213
|
+
type: MetadataEnums.deploy,
|
|
214
|
+
encodedData: '0x',
|
|
215
|
+
})
|
|
134
216
|
|
|
135
|
-
return single ? encodeMultipleActions(data) : data
|
|
136
|
-
}
|
|
217
|
+
return single ? encodeMultipleActions(data) : data
|
|
218
|
+
}
|
|
137
219
|
|
|
138
|
-
export
|
|
139
|
-
params: SignMetadataProps,
|
|
140
|
-
single = true
|
|
141
|
-
) => {
|
|
220
|
+
export function encodeTransactionBuilderMetadata(actionCount: string, single = true) {
|
|
142
221
|
const encodedData = ethers.utils.defaultAbiCoder.encode(
|
|
143
|
-
actionMetadataTypes[
|
|
144
|
-
[
|
|
145
|
-
)
|
|
222
|
+
actionMetadataTypes['tx-builder'],
|
|
223
|
+
[actionCount],
|
|
224
|
+
)
|
|
146
225
|
|
|
147
226
|
const data = encodeMetadata({
|
|
148
|
-
type:
|
|
227
|
+
type: MetadataEnums['tx-builder'],
|
|
149
228
|
encodedData,
|
|
150
|
-
})
|
|
229
|
+
})
|
|
151
230
|
|
|
152
|
-
return single ? encodeMultipleActions(data) : data
|
|
153
|
-
}
|
|
231
|
+
return single ? encodeMultipleActions(data) : data
|
|
232
|
+
}
|
|
154
233
|
|
|
155
|
-
export
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
234
|
+
export function encodeWCSignMetadata(params: SignMetadataProps, single = true) {
|
|
235
|
+
const encodedData = ethers.utils.defaultAbiCoder.encode(
|
|
236
|
+
actionMetadataTypes.permit2,
|
|
237
|
+
[params.token, params.spender, params.amount, params.expiration],
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
const data = encodeMetadata({
|
|
241
|
+
type: MetadataEnums.permit2,
|
|
242
|
+
encodedData,
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
return single ? encodeMultipleActions(data) : data
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export function encodeUpgradeMetadata(params: UpgradeMetadataProps, single = true) {
|
|
159
249
|
const encodedData = ethers.utils.defaultAbiCoder.encode(
|
|
160
250
|
actionMetadataTypes.upgrade,
|
|
161
|
-
[params.version, params.walletImpl]
|
|
162
|
-
)
|
|
251
|
+
[params.version, params.walletImpl],
|
|
252
|
+
)
|
|
163
253
|
|
|
164
254
|
const data = encodeMetadata({
|
|
165
|
-
type:
|
|
255
|
+
type: MetadataEnums.upgrade,
|
|
166
256
|
encodedData,
|
|
167
|
-
})
|
|
257
|
+
})
|
|
168
258
|
|
|
169
|
-
return single ? encodeMultipleActions(data) : data
|
|
170
|
-
}
|
|
259
|
+
return single ? encodeMultipleActions(data) : data
|
|
260
|
+
}
|
|
171
261
|
|
|
172
|
-
export
|
|
173
|
-
params: SwapMetadataProps,
|
|
174
|
-
single = true
|
|
175
|
-
) => {
|
|
262
|
+
export function encodeSwapMetadata(params: SwapMetadataProps, single = true) {
|
|
176
263
|
const encodedData = ethers.utils.defaultAbiCoder.encode(
|
|
177
264
|
actionMetadataTypes.swap,
|
|
178
265
|
[
|
|
@@ -182,228 +269,389 @@ export const encodeSwapMetadata = (
|
|
|
182
269
|
params.buyAmount,
|
|
183
270
|
params.receiver,
|
|
184
271
|
params.protocol,
|
|
185
|
-
]
|
|
186
|
-
)
|
|
272
|
+
],
|
|
273
|
+
)
|
|
187
274
|
|
|
188
275
|
const data = encodeMetadata({
|
|
189
|
-
type:
|
|
276
|
+
type: MetadataEnums.swap,
|
|
190
277
|
encodedData,
|
|
191
|
-
})
|
|
278
|
+
})
|
|
192
279
|
|
|
193
|
-
return single ? encodeMultipleActions(data) : data
|
|
194
|
-
}
|
|
280
|
+
return single ? encodeMultipleActions(data) : data
|
|
281
|
+
}
|
|
195
282
|
|
|
196
|
-
export
|
|
197
|
-
params: TopupMetadataProps,
|
|
198
|
-
single = true
|
|
199
|
-
) => {
|
|
283
|
+
export function encodeTopupMetadata(params: TopupMetadataProps, single = true) {
|
|
200
284
|
const encodedData = ethers.utils.defaultAbiCoder.encode(
|
|
201
|
-
actionMetadataTypes[
|
|
202
|
-
[params.amount, params.token, params.onBehalf]
|
|
203
|
-
)
|
|
285
|
+
actionMetadataTypes['gas-topup'],
|
|
286
|
+
[params.amount, params.token, params.onBehalf],
|
|
287
|
+
)
|
|
204
288
|
|
|
205
|
-
|
|
289
|
+
const data = encodeMetadata({
|
|
290
|
+
type: MetadataEnums['gas-topup'],
|
|
291
|
+
encodedData,
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
return single ? encodeMultipleActions(data) : data
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export function encodeBridgeMetadata(params: BridgeMetadataProps, single = true) {
|
|
298
|
+
const type = params.version === '2' ? MetadataEnums['bridge-v2'] : MetadataEnums.bridge
|
|
299
|
+
|
|
300
|
+
const encodedData = params.version === '2'
|
|
301
|
+
? ethers.utils.defaultAbiCoder.encode(
|
|
302
|
+
actionMetadataTypes['bridge-v2'],
|
|
303
|
+
[
|
|
304
|
+
params.amount,
|
|
305
|
+
params.receiver,
|
|
306
|
+
params.fromToken,
|
|
307
|
+
params.toToken,
|
|
308
|
+
params.toChainId,
|
|
309
|
+
params.bridgeFee,
|
|
310
|
+
params.nativeToken,
|
|
311
|
+
params.provider,
|
|
312
|
+
],
|
|
313
|
+
)
|
|
314
|
+
: ethers.utils.defaultAbiCoder.encode(
|
|
315
|
+
actionMetadataTypes.bridge,
|
|
316
|
+
[
|
|
317
|
+
params.amount,
|
|
318
|
+
params.receiver,
|
|
319
|
+
params.fromToken,
|
|
320
|
+
params.toToken,
|
|
321
|
+
params.toChainId,
|
|
322
|
+
params.bridgeFee,
|
|
323
|
+
params.nativeToken,
|
|
324
|
+
],
|
|
325
|
+
)
|
|
206
326
|
|
|
207
327
|
const data = encodeMetadata({
|
|
208
|
-
type
|
|
328
|
+
type,
|
|
209
329
|
encodedData,
|
|
210
|
-
})
|
|
330
|
+
})
|
|
211
331
|
|
|
212
|
-
return single ? encodeMultipleActions(data) : data
|
|
213
|
-
}
|
|
332
|
+
return single ? encodeMultipleActions(data) : data
|
|
333
|
+
}
|
|
214
334
|
|
|
215
|
-
export
|
|
216
|
-
params: BridgeMetadataProps,
|
|
217
|
-
single = true
|
|
218
|
-
) => {
|
|
335
|
+
export function encodeChangeThresholdMetadata(threshold: string | number, single = true) {
|
|
219
336
|
const encodedData = ethers.utils.defaultAbiCoder.encode(
|
|
220
|
-
actionMetadataTypes
|
|
221
|
-
[
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
337
|
+
actionMetadataTypes['change-threshold'],
|
|
338
|
+
[toBN(threshold).toNumber()],
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
const data = encodeMetadata({
|
|
342
|
+
type: MetadataEnums['change-threshold'],
|
|
343
|
+
encodedData,
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
return single ? encodeMultipleActions(data) : data
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
export function encodeRemoveSignersMetadata(addresses: string[], single = true) {
|
|
350
|
+
const encodedData = ethers.utils.defaultAbiCoder.encode(
|
|
351
|
+
actionMetadataTypes['remove-signers'],
|
|
352
|
+
[addresses],
|
|
353
|
+
)
|
|
231
354
|
|
|
232
355
|
const data = encodeMetadata({
|
|
233
|
-
type:
|
|
356
|
+
type: MetadataEnums['remove-signers'],
|
|
234
357
|
encodedData,
|
|
235
|
-
})
|
|
358
|
+
})
|
|
236
359
|
|
|
237
|
-
return single ? encodeMultipleActions(data) : data
|
|
238
|
-
}
|
|
360
|
+
return single ? encodeMultipleActions(data) : data
|
|
361
|
+
}
|
|
239
362
|
|
|
240
|
-
export
|
|
241
|
-
|
|
242
|
-
|
|
363
|
+
export function encodeImportMetadata(protocol: string, valueInUsd: string, single = true) {
|
|
364
|
+
const encodedData = ethers.utils.defaultAbiCoder.encode(
|
|
365
|
+
actionMetadataTypes.import,
|
|
366
|
+
[protocol, valueInUsd],
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
const data = encodeMetadata({
|
|
370
|
+
type: MetadataEnums.import,
|
|
371
|
+
encodedData,
|
|
372
|
+
})
|
|
243
373
|
|
|
244
|
-
|
|
374
|
+
return single ? encodeMultipleActions(data) : data
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
export function encodeMassMetadata(single = true) {
|
|
378
|
+
const data = encodeMetadata({
|
|
379
|
+
type: MetadataEnums.mass,
|
|
380
|
+
encodedData: '0x',
|
|
381
|
+
})
|
|
382
|
+
|
|
383
|
+
return single ? encodeMultipleActions(data) : data
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export function encodeAddSignersMetadata(addresses: string[], single = true) {
|
|
387
|
+
const encodedData = ethers.utils.defaultAbiCoder.encode(
|
|
388
|
+
actionMetadataTypes['add-signers'],
|
|
389
|
+
[addresses],
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
const data = encodeMetadata({
|
|
393
|
+
type: MetadataEnums['add-signers'],
|
|
394
|
+
encodedData,
|
|
395
|
+
})
|
|
396
|
+
|
|
397
|
+
return single ? encodeMultipleActions(data) : data
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
export function encodeMultipleActions(...actionData: string[]) {
|
|
401
|
+
return ethers.utils.defaultAbiCoder.encode(multiMetadataTypes, [actionData])
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
export function decodeData(data: string) {
|
|
245
405
|
try {
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
}
|
|
269
|
-
} else {
|
|
270
|
-
const executeDataV3 = iface.decodeFunctionData("executeV3", data);
|
|
271
|
-
if (
|
|
272
|
-
executeDataV3.params_.metadata === "0x" ||
|
|
273
|
-
!executeDataV3.params_.metadata
|
|
274
|
-
) {
|
|
275
|
-
return null;
|
|
276
|
-
} else {
|
|
277
|
-
metadata = executeDataV3.params_.metadata;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
406
|
+
const metadata = getMetadataFromData(data) || '0x'
|
|
407
|
+
|
|
408
|
+
return parseMetadata(metadata)
|
|
409
|
+
}
|
|
410
|
+
catch {
|
|
411
|
+
// console.log(e);
|
|
412
|
+
return null
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
export function decodeMetadata(metadata: string) {
|
|
417
|
+
try {
|
|
418
|
+
return parseMetadata(metadata)
|
|
419
|
+
}
|
|
420
|
+
catch {
|
|
421
|
+
return null
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const iface = Forwarder__factory.createInterface()
|
|
426
|
+
const ifaceMultisig = MultisigForwarder__factory.createInterface()
|
|
427
|
+
const ifaceAgnostic = MultisigAgnosticForwarder__factory.createInterface()
|
|
280
428
|
|
|
281
|
-
|
|
429
|
+
function getMetadataFromData(data: string) {
|
|
430
|
+
let metadata = '0x'
|
|
282
431
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
multiMetadataTypes,
|
|
286
|
-
metadata
|
|
287
|
-
) as string[]) || [];
|
|
432
|
+
if (data.startsWith('0xc5e15557')) {
|
|
433
|
+
const executeData = ifaceAgnostic.decodeFunctionData('executeChainAgnosticV1', data)
|
|
288
434
|
|
|
289
|
-
|
|
290
|
-
const decodedMetadata = ethers.utils.defaultAbiCoder.decode(
|
|
291
|
-
metadataTypes,
|
|
292
|
-
metadata
|
|
293
|
-
);
|
|
435
|
+
const metadata = executeData?.params_?.params?.metadata
|
|
294
436
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
437
|
+
return metadata === '0x' || !metadata ? null : metadata
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
else if (data.startsWith('0x18e7f485')) {
|
|
441
|
+
const executeData = iface.decodeFunctionData('execute', data)
|
|
442
|
+
if (executeData.metadata_ === '0x' || !executeData.metadata_) {
|
|
443
|
+
return null
|
|
444
|
+
}
|
|
445
|
+
else {
|
|
446
|
+
metadata = executeData.metadata_
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
else if (data.startsWith('0x14f80a8d')) {
|
|
450
|
+
const executeDataV2 = iface.decodeFunctionData('executeV2', data)
|
|
451
|
+
if (
|
|
452
|
+
executeDataV2.params_.metadata === '0x'
|
|
453
|
+
|| !executeDataV2.params_.metadata
|
|
454
|
+
) {
|
|
455
|
+
return null
|
|
456
|
+
}
|
|
457
|
+
else {
|
|
458
|
+
metadata = executeDataV2.params_.metadata
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
else {
|
|
462
|
+
const executeDataMultisig = ifaceMultisig.decodeFunctionData(
|
|
463
|
+
'executeV1',
|
|
464
|
+
data,
|
|
465
|
+
)
|
|
466
|
+
if (
|
|
467
|
+
executeDataMultisig.params_.metadata === '0x'
|
|
468
|
+
|| !executeDataMultisig.params_.metadata
|
|
469
|
+
) {
|
|
470
|
+
return null
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
metadata = executeDataMultisig.params_.metadata
|
|
474
|
+
}
|
|
475
|
+
}
|
|
298
476
|
|
|
299
|
-
|
|
477
|
+
return metadata
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const typesPayload: IPayload = {
|
|
481
|
+
'import': (data, type) => ({
|
|
482
|
+
type,
|
|
483
|
+
protocol: utils.parseBytes32String(data.protocol || ''),
|
|
484
|
+
valueInUsd: toBN(data.valueInUsd).toFixed(),
|
|
485
|
+
}),
|
|
486
|
+
'transfer': (data, type) => ({
|
|
487
|
+
type,
|
|
488
|
+
token: data.token,
|
|
489
|
+
amount: toBN(data.amount).toFixed(),
|
|
490
|
+
receiver: data.receiver,
|
|
491
|
+
}),
|
|
492
|
+
'bridge': (data, type) => ({
|
|
493
|
+
type,
|
|
494
|
+
amount: toBN(data.amount).toFixed(),
|
|
495
|
+
receiver: data.receiver,
|
|
496
|
+
toToken: data.toToken,
|
|
497
|
+
fromToken: data.fromToken,
|
|
498
|
+
toChainId: data.toChainId ? data.toChainId.toString() : null,
|
|
499
|
+
bridgeFee: toBN(data.bridgeFee).toFixed(),
|
|
500
|
+
}),
|
|
501
|
+
'bridge-v2': data => ({
|
|
502
|
+
type: 'bridge',
|
|
503
|
+
amount: toBN(data.amount).toFixed(),
|
|
504
|
+
receiver: data.receiver,
|
|
505
|
+
toToken: data.toToken,
|
|
506
|
+
fromToken: data.fromToken,
|
|
507
|
+
toChainId: data.toChainId ? data.toChainId.toString() : null,
|
|
508
|
+
bridgeFee: toBN(data.bridgeFee).toFixed(),
|
|
509
|
+
provider: utils.parseBytes32String(data.provider || ''),
|
|
510
|
+
}),
|
|
511
|
+
'swap': (data, type) => ({
|
|
512
|
+
type,
|
|
513
|
+
buyAmount: toBN(data.buyAmount).toFixed(),
|
|
514
|
+
sellAmount: toBN(data.sellAmount).toFixed(),
|
|
515
|
+
buyToken: data.buyToken,
|
|
516
|
+
sellToken: data.sellToken,
|
|
517
|
+
receiver: data.receiver,
|
|
518
|
+
protocol: utils.parseBytes32String(data.protocol || ''),
|
|
519
|
+
}),
|
|
520
|
+
'upgrade': (data, type) => ({
|
|
521
|
+
type,
|
|
522
|
+
version: utils.parseBytes32String(data.version || ''),
|
|
523
|
+
walletImpl: data.walletImpl,
|
|
524
|
+
}),
|
|
525
|
+
'gas-topup': (data, type) => ({
|
|
526
|
+
type,
|
|
527
|
+
amount: toBN(data.amount).toFixed(),
|
|
528
|
+
token: data.token,
|
|
529
|
+
onBehalf: data.onBehalf,
|
|
530
|
+
}),
|
|
531
|
+
'dapp': (data, type) => ({
|
|
532
|
+
type,
|
|
533
|
+
name: data.name,
|
|
534
|
+
url: data.url,
|
|
535
|
+
}),
|
|
536
|
+
'deploy': (data, type) => ({
|
|
537
|
+
type,
|
|
538
|
+
}),
|
|
539
|
+
'tx-builder': (data, type) => ({
|
|
540
|
+
type,
|
|
541
|
+
actionCount: utils.parseBytes32String(data.actionCount || ''),
|
|
542
|
+
}),
|
|
543
|
+
'permit2': (data, type) => ({
|
|
544
|
+
type,
|
|
545
|
+
token: data.token,
|
|
546
|
+
spender: data.spender,
|
|
547
|
+
amount: toBN(data.amount).toFixed(),
|
|
548
|
+
expiration: data.expiration,
|
|
549
|
+
}),
|
|
550
|
+
'cross-transfer': (data, type) => ({
|
|
551
|
+
type,
|
|
552
|
+
fromToken: data.fromToken,
|
|
553
|
+
toToken: data.toToken,
|
|
554
|
+
toChainId: data.toChainId ? data.toChainId.toString() : null,
|
|
555
|
+
amount: toBN(data.amount).toFixed(),
|
|
556
|
+
receiver: data.receiver,
|
|
557
|
+
}),
|
|
558
|
+
'cross-transfer-v2': data => ({
|
|
559
|
+
type: 'cross-transfer',
|
|
560
|
+
fromToken: data.fromToken,
|
|
561
|
+
toToken: data.toToken,
|
|
562
|
+
toChainId: data.toChainId ? data.toChainId.toString() : null,
|
|
563
|
+
amount: toBN(data.amount).toFixed(),
|
|
564
|
+
receiver: data.receiver,
|
|
565
|
+
provider: utils.parseBytes32String(data.provider || ''),
|
|
566
|
+
}),
|
|
567
|
+
'auth': data => ({
|
|
568
|
+
type: data.remove ? 'remove-authority' : 'add-authority',
|
|
569
|
+
address: data.address,
|
|
570
|
+
chainId: data.chainId ? data.chainId.toString() : null,
|
|
571
|
+
remove: data.remove,
|
|
572
|
+
}),
|
|
573
|
+
'instadapp-pro': (data, type) => ({
|
|
574
|
+
type,
|
|
575
|
+
castDetails: data.castDetails,
|
|
576
|
+
}),
|
|
577
|
+
'rejection': (data, type) => ({
|
|
578
|
+
type,
|
|
579
|
+
id: data.id,
|
|
580
|
+
}),
|
|
581
|
+
'add-signers': (data, type) => ({
|
|
582
|
+
type,
|
|
583
|
+
addresses: data.signers,
|
|
584
|
+
}),
|
|
585
|
+
'remove-signers': (data, type) => ({
|
|
586
|
+
type,
|
|
587
|
+
addresses: data.signers,
|
|
588
|
+
}),
|
|
589
|
+
'change-threshold': (data, type) => ({
|
|
590
|
+
type,
|
|
591
|
+
count: data.count,
|
|
592
|
+
}),
|
|
593
|
+
'avocado-bridge': (data, type) => ({
|
|
594
|
+
type,
|
|
595
|
+
id: data.id,
|
|
596
|
+
toChainId: data.toChainId ? data.toChainId.toString() : null,
|
|
597
|
+
}),
|
|
598
|
+
'mass': (data, type) => ({
|
|
599
|
+
type,
|
|
600
|
+
data,
|
|
601
|
+
}),
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
function parseMetadata(metadata: string) {
|
|
605
|
+
const metadataArr = []
|
|
606
|
+
|
|
607
|
+
const [decodedMultiMetadata = []]
|
|
608
|
+
= (ethers.utils.defaultAbiCoder.decode(
|
|
609
|
+
multiMetadataTypes,
|
|
610
|
+
metadata,
|
|
611
|
+
) as string[]) || []
|
|
612
|
+
|
|
613
|
+
for (const metadata of decodedMultiMetadata) {
|
|
614
|
+
const decodedMetadata = ethers.utils.defaultAbiCoder.decode(
|
|
615
|
+
metadataTypes,
|
|
616
|
+
metadata,
|
|
617
|
+
)
|
|
618
|
+
|
|
619
|
+
const type = ethers.utils.parseBytes32String(
|
|
620
|
+
decodedMetadata.type,
|
|
621
|
+
) as keyof typeof actionMetadataTypes
|
|
622
|
+
|
|
623
|
+
const decodedData = decodedMetadata?.data === '0x'
|
|
624
|
+
? ''
|
|
625
|
+
: ethers.utils.defaultAbiCoder.decode(
|
|
300
626
|
actionMetadataTypes[type],
|
|
301
|
-
decodedMetadata.data
|
|
302
|
-
)
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
amount: toBN(decodedData.amount).toFixed(),
|
|
310
|
-
receiver: decodedData.receiver,
|
|
311
|
-
};
|
|
312
|
-
break;
|
|
313
|
-
case "bridge":
|
|
314
|
-
payload = {
|
|
315
|
-
type,
|
|
316
|
-
amount: toBN(decodedData.amount).toFixed(),
|
|
317
|
-
receiver: decodedData.receiver,
|
|
318
|
-
toToken: decodedData.toToken,
|
|
319
|
-
fromToken: decodedData.fromToken,
|
|
320
|
-
toChainId: decodedData.toChainId
|
|
321
|
-
? decodedData.toChainId.toString()
|
|
322
|
-
: null,
|
|
323
|
-
bridgeFee: toBN(decodedData.bridgeFee).toFixed(),
|
|
324
|
-
};
|
|
325
|
-
break;
|
|
326
|
-
case "swap":
|
|
327
|
-
payload = {
|
|
328
|
-
type,
|
|
329
|
-
buyAmount: toBN(decodedData.buyAmount).toFixed(),
|
|
330
|
-
sellAmount: toBN(decodedData.sellAmount).toFixed(),
|
|
331
|
-
buyToken: decodedData.buyToken,
|
|
332
|
-
sellToken: decodedData.sellToken,
|
|
333
|
-
receiver: decodedData.receiver,
|
|
334
|
-
protocol: utils.parseBytes32String(decodedData?.protocol || ""),
|
|
335
|
-
};
|
|
336
|
-
break;
|
|
337
|
-
case "upgrade":
|
|
338
|
-
payload = {
|
|
339
|
-
type,
|
|
340
|
-
version: utils.parseBytes32String(decodedData?.version || ""),
|
|
341
|
-
walletImpl: decodedData?.walletImpl,
|
|
342
|
-
};
|
|
343
|
-
break;
|
|
344
|
-
case "gas-topup":
|
|
345
|
-
payload = {
|
|
346
|
-
type,
|
|
347
|
-
amount: toBN(decodedData.amount).toFixed(),
|
|
348
|
-
token: decodedData.token,
|
|
349
|
-
onBehalf: decodedData.onBehalf,
|
|
350
|
-
};
|
|
351
|
-
break;
|
|
352
|
-
case "dapp":
|
|
353
|
-
payload = {
|
|
354
|
-
type,
|
|
355
|
-
name: decodedData?.name,
|
|
356
|
-
url: decodedData?.url,
|
|
357
|
-
};
|
|
358
|
-
break;
|
|
359
|
-
case "deploy":
|
|
360
|
-
payload = {
|
|
361
|
-
type,
|
|
362
|
-
};
|
|
363
|
-
break;
|
|
364
|
-
|
|
365
|
-
case "permit2":
|
|
366
|
-
payload = {
|
|
367
|
-
type,
|
|
368
|
-
token: decodedData.token,
|
|
369
|
-
spender: decodedData.spender,
|
|
370
|
-
amount: toBN(decodedData.amount).toFixed(),
|
|
371
|
-
expiration: decodedData.expiration,
|
|
372
|
-
};
|
|
373
|
-
break;
|
|
374
|
-
|
|
375
|
-
case "cross-transfer":
|
|
376
|
-
payload = {
|
|
377
|
-
type,
|
|
378
|
-
fromToken: decodedData.fromToken,
|
|
379
|
-
toToken: decodedData.toToken,
|
|
380
|
-
toChainId: decodedData.toChainId
|
|
381
|
-
? decodedData.toChainId.toString()
|
|
382
|
-
: null,
|
|
383
|
-
amount: toBN(decodedData.amount).toFixed(),
|
|
384
|
-
receiver: decodedData.receiver,
|
|
385
|
-
};
|
|
386
|
-
|
|
387
|
-
break;
|
|
388
|
-
case "auth":
|
|
389
|
-
payload = {
|
|
390
|
-
type: decodedData.remove ? "remove-authority" : "add-authority",
|
|
391
|
-
address: decodedData.address,
|
|
392
|
-
chainId: decodedData.chainId
|
|
393
|
-
? decodedData.chainId.toString()
|
|
394
|
-
: null,
|
|
395
|
-
remove: decodedData.remove,
|
|
396
|
-
};
|
|
397
|
-
|
|
398
|
-
break;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
metadataArr.push(payload);
|
|
627
|
+
decodedMetadata.data,
|
|
628
|
+
)
|
|
629
|
+
|
|
630
|
+
const payloadFunc = typesPayload[type]
|
|
631
|
+
|
|
632
|
+
if (payloadFunc) {
|
|
633
|
+
const payload = payloadFunc(decodedData, type)
|
|
634
|
+
metadataArr.push(payload)
|
|
402
635
|
}
|
|
636
|
+
}
|
|
403
637
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
638
|
+
return metadataArr
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* Replaces hyphens with spaces and capitalizes the first letter of each word in a sentence.
|
|
643
|
+
* @param {string} txType - The input sentence to modify
|
|
644
|
+
*
|
|
645
|
+
* @returns {string} - The modified sentence with hyphens replaced with spaces and the first letter of each word capitalized.
|
|
646
|
+
*/
|
|
647
|
+
export function formatTxType(txType: string) {
|
|
648
|
+
if (txType === 'mass') {
|
|
649
|
+
return 'Chain Agnostic Payments'
|
|
408
650
|
}
|
|
409
|
-
|
|
651
|
+
|
|
652
|
+
const finalSentence = txType
|
|
653
|
+
.replace('-', ' ')
|
|
654
|
+
.replace(/(^\w)|(\s+\w)/g, letter => letter.toUpperCase())
|
|
655
|
+
|
|
656
|
+
return finalSentence
|
|
657
|
+
}
|