@wharfkit/transact-plugin-resource-provider 0.3.0-ui-5 → 0.3.0-ui-16
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/README.md +53 -2
- package/lib/transact-plugin-resource-provider.d.ts +44 -5
- package/lib/transact-plugin-resource-provider.js +168 -80
- package/lib/transact-plugin-resource-provider.js.map +1 -1
- package/lib/transact-plugin-resource-provider.m.js +160 -63
- package/lib/transact-plugin-resource-provider.m.js.map +1 -1
- package/package.json +6 -4
- package/src/index.ts +157 -48
- package/src/translations.json +30 -0
- package/lib/resource-provider-plugin.d.ts +0 -84
- package/lib/resource-provider-plugin.js +0 -322
- package/lib/resource-provider-plugin.js.map +0 -1
- package/lib/resource-provider-plugin.m.js +0 -302
- package/lib/resource-provider-plugin.m.js.map +0 -1
- package/lib/resource-provider-plugin.umd.js +0 -303
package/src/index.ts
CHANGED
|
@@ -3,8 +3,11 @@ import {
|
|
|
3
3
|
Action,
|
|
4
4
|
Asset,
|
|
5
5
|
AssetType,
|
|
6
|
+
Cancelable,
|
|
7
|
+
Canceled,
|
|
6
8
|
ChainDefinition,
|
|
7
9
|
Name,
|
|
10
|
+
PromptResponse,
|
|
8
11
|
Serializer,
|
|
9
12
|
Signature,
|
|
10
13
|
SigningRequest,
|
|
@@ -15,12 +18,13 @@ import {
|
|
|
15
18
|
Transaction,
|
|
16
19
|
} from '@wharfkit/session'
|
|
17
20
|
|
|
21
|
+
import defaultTranslations from './translations.json'
|
|
18
22
|
import {getNewActions, hasOriginalActions} from './utils'
|
|
19
23
|
|
|
20
24
|
interface ResourceProviderOptions {
|
|
21
25
|
allowFees?: boolean
|
|
22
26
|
// allowActions?: NameType[]
|
|
23
|
-
endpoints
|
|
27
|
+
endpoints?: Record<string, string>
|
|
24
28
|
maxFee?: AssetType
|
|
25
29
|
}
|
|
26
30
|
|
|
@@ -49,35 +53,60 @@ export class Transfer extends Struct {
|
|
|
49
53
|
@Struct.field('string') memo!: string
|
|
50
54
|
}
|
|
51
55
|
|
|
52
|
-
export
|
|
53
|
-
|
|
56
|
+
export const defaultOptions = {
|
|
57
|
+
endpoints: {
|
|
58
|
+
aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906:
|
|
59
|
+
'https://eos.greymass.com',
|
|
60
|
+
'73e4385a2708e6d7048834fbc1079f2fabb17b3c125b146af438971e90716c4d':
|
|
61
|
+
'https://jungle4.greymass.com',
|
|
62
|
+
'4667b205c6838ef70ff7988f6e8257e8be0e1284a2f59699054a018f743b1d11':
|
|
63
|
+
'https://telos.greymass.com',
|
|
64
|
+
'1064487b3cd1a897ce03ae5b6a865651747e2e152090f99c1d19d44e01aea5a4':
|
|
65
|
+
'https://wax.greymass.com',
|
|
66
|
+
},
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export class TransactPluginResourceProvider extends AbstractTransactPlugin {
|
|
70
|
+
id = 'transact-plugin-resource-provider'
|
|
71
|
+
translations = defaultTranslations
|
|
72
|
+
readonly allowFees: boolean = true
|
|
54
73
|
// readonly allowActions: Name[] = [
|
|
55
74
|
// Name.from('eosio.token:transfer'),
|
|
56
75
|
// Name.from('eosio:buyrambytes'),
|
|
57
76
|
// ]
|
|
58
77
|
readonly maxFee?: Asset
|
|
59
78
|
|
|
60
|
-
readonly endpoints: Record<string, string> =
|
|
79
|
+
readonly endpoints: Record<string, string> = defaultOptions.endpoints
|
|
61
80
|
|
|
62
|
-
constructor(options
|
|
81
|
+
constructor(options?: ResourceProviderOptions) {
|
|
63
82
|
super()
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
83
|
+
if (options) {
|
|
84
|
+
// Set the endpoints and chains available
|
|
85
|
+
if (options.endpoints) {
|
|
86
|
+
this.endpoints = options.endpoints
|
|
87
|
+
}
|
|
88
|
+
if (typeof options.allowFees !== 'undefined') {
|
|
89
|
+
this.allowFees = options.allowFees
|
|
90
|
+
}
|
|
91
|
+
if (typeof options.maxFee !== 'undefined') {
|
|
92
|
+
this.maxFee = Asset.from(options.maxFee)
|
|
93
|
+
}
|
|
94
|
+
// TODO: Allow contact/action combos to be passed in and checked against to ensure no rogue actions were appended.
|
|
95
|
+
// if (typeof options.allowActions !== 'undefined') {
|
|
96
|
+
// this.allowActions = options.allowActions.map((action) => Name.from(action))
|
|
97
|
+
// }
|
|
75
98
|
}
|
|
76
99
|
}
|
|
77
100
|
|
|
78
101
|
register(context: TransactContext): void {
|
|
79
|
-
context.addHook(
|
|
80
|
-
|
|
102
|
+
context.addHook(
|
|
103
|
+
TransactHookTypes.beforeSign,
|
|
104
|
+
async (
|
|
105
|
+
request: SigningRequest,
|
|
106
|
+
context: TransactContext
|
|
107
|
+
): Promise<TransactHookResponse> => {
|
|
108
|
+
return this.request(request, context)
|
|
109
|
+
}
|
|
81
110
|
)
|
|
82
111
|
}
|
|
83
112
|
|
|
@@ -89,6 +118,13 @@ export default class ResourceProviderPlugin extends AbstractTransactPlugin {
|
|
|
89
118
|
request: SigningRequest,
|
|
90
119
|
context: TransactContext
|
|
91
120
|
): Promise<TransactHookResponse> {
|
|
121
|
+
// Mock the translation function if no UI is available
|
|
122
|
+
let t = (key: string, options: {default: string; [key: string]: unknown}) => options.default
|
|
123
|
+
if (context.ui) {
|
|
124
|
+
// Use the translate function if available
|
|
125
|
+
t = context.ui.getTranslate(this.id)
|
|
126
|
+
}
|
|
127
|
+
|
|
92
128
|
// Validate that this request is valid for the resource provider
|
|
93
129
|
this.validateRequest(request, context)
|
|
94
130
|
|
|
@@ -127,8 +163,17 @@ export default class ResourceProviderPlugin extends AbstractTransactPlugin {
|
|
|
127
163
|
if (requiresPayment) {
|
|
128
164
|
// If the resource provider offered transaction with a fee, but plugin doesn't allow fees, return the original transaction.
|
|
129
165
|
if (!this.allowFees) {
|
|
130
|
-
// TODO: Notify the script somehow of this, maybe we need an optional logger?
|
|
131
166
|
// Notify that a fee was required but not allowed via allowFees: false.
|
|
167
|
+
if (context.ui) {
|
|
168
|
+
context.ui.status(
|
|
169
|
+
`${t('rejected.no-fees', {
|
|
170
|
+
default:
|
|
171
|
+
'A resource provider offered to cover this transaction for a fee, but fee-based transactions are disabled by the configuration using `allowFees = false`.',
|
|
172
|
+
})} ${t('will-continue', {
|
|
173
|
+
default: 'The transaction will continue without the resource provider.',
|
|
174
|
+
})}`
|
|
175
|
+
)
|
|
176
|
+
}
|
|
132
177
|
return {
|
|
133
178
|
request,
|
|
134
179
|
}
|
|
@@ -144,8 +189,17 @@ export default class ResourceProviderPlugin extends AbstractTransactPlugin {
|
|
|
144
189
|
)
|
|
145
190
|
|
|
146
191
|
if (!originalActionsIntact) {
|
|
147
|
-
// TODO: Notify the script somehow of this, maybe we need an optional logger?
|
|
148
192
|
// Notify that the original actions requested were modified somehow, and reject the modification.
|
|
193
|
+
if (context.ui) {
|
|
194
|
+
context.ui.status(
|
|
195
|
+
`${t('rejected.original-modified', {
|
|
196
|
+
default:
|
|
197
|
+
'The original transaction returned by the resource provider has been modified too much. Continuing without the resource provider',
|
|
198
|
+
})} ${t('will-continue', {
|
|
199
|
+
default: 'The transaction will continue without the resource provider.',
|
|
200
|
+
})}`
|
|
201
|
+
)
|
|
202
|
+
}
|
|
149
203
|
return {
|
|
150
204
|
request,
|
|
151
205
|
}
|
|
@@ -177,8 +231,17 @@ export default class ResourceProviderPlugin extends AbstractTransactPlugin {
|
|
|
177
231
|
// If the resource provider offered transaction with a fee, but the fee was higher than allowed, return the original transaction.
|
|
178
232
|
if (this.maxFee) {
|
|
179
233
|
if (addedFees.units > this.maxFee.units) {
|
|
180
|
-
// TODO: Notify the script somehow of this, maybe we need an optional logger?
|
|
181
234
|
// Notify that a fee was required but higher than allowed via maxFee.
|
|
235
|
+
if (context.ui) {
|
|
236
|
+
context.ui.status(
|
|
237
|
+
`${t('rejected.max-fee', {
|
|
238
|
+
default:
|
|
239
|
+
'The fee requested by the resource provider is unusually high and has been rejected.',
|
|
240
|
+
})} ${t('will-continue', {
|
|
241
|
+
default: 'The transaction will continue without the resource provider.',
|
|
242
|
+
})}`
|
|
243
|
+
)
|
|
244
|
+
}
|
|
182
245
|
return {
|
|
183
246
|
request,
|
|
184
247
|
}
|
|
@@ -188,34 +251,83 @@ export default class ResourceProviderPlugin extends AbstractTransactPlugin {
|
|
|
188
251
|
// Validate that the response is valid for the session.
|
|
189
252
|
await this.validateResponseData(json)
|
|
190
253
|
|
|
191
|
-
//
|
|
192
|
-
|
|
193
|
-
/* Psuedo-code for fee based prompting
|
|
194
|
-
|
|
195
|
-
if (response.status === 402) {
|
|
196
|
-
|
|
197
|
-
// Prompt for the fee acceptance
|
|
198
|
-
const promptResponse = context.userPrompt({
|
|
199
|
-
title: 'Transaction Fee Required',
|
|
200
|
-
message: `This transaction requires a fee of ${response.json.data.fee} EOS. Do you wish to accept this fee?`,
|
|
201
|
-
})
|
|
254
|
+
// Create a new signing request based on the response to return to the session's transact flow.
|
|
255
|
+
const modified = await this.createRequest(json, context)
|
|
202
256
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
}
|
|
257
|
+
if (context.ui && addedFees.value > 0) {
|
|
258
|
+
// Determine which resources are being covered by this fee
|
|
259
|
+
const resourceTypes: string[] = []
|
|
260
|
+
if (json.data.costs) {
|
|
261
|
+
const {cpu, net, ram} = json.data.costs
|
|
262
|
+
if (Asset.from(cpu).value > 0) resourceTypes.push('CPU')
|
|
263
|
+
if (Asset.from(net).value > 0) resourceTypes.push('NET')
|
|
264
|
+
if (Asset.from(ram).value > 0) resourceTypes.push('RAM')
|
|
265
|
+
} else {
|
|
266
|
+
resourceTypes.push('Unknown')
|
|
208
267
|
}
|
|
209
|
-
|
|
268
|
+
// Initiate a new cancelable prompt to inform the user of the fee required
|
|
269
|
+
const prompt: Cancelable<PromptResponse> = context.ui.prompt({
|
|
270
|
+
title: t('fee.title', {default: 'Accept Transaction Fee?'}),
|
|
271
|
+
body: t('fee.body', {
|
|
272
|
+
default:
|
|
273
|
+
'Additional resources ({{resource}}) are required for your account to perform this transaction. Would you like to automatically purchase these resources and proceed?',
|
|
274
|
+
resource: String(resourceTypes.join('/')),
|
|
275
|
+
}),
|
|
276
|
+
elements: [
|
|
277
|
+
{
|
|
278
|
+
type: 'asset',
|
|
279
|
+
data: {
|
|
280
|
+
label: t('fee.cost', {
|
|
281
|
+
default: 'Cost of {{resource}}',
|
|
282
|
+
resource: String(resourceTypes.join('/')),
|
|
283
|
+
}),
|
|
284
|
+
value: addedFees,
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
type: 'accept',
|
|
289
|
+
},
|
|
290
|
+
],
|
|
291
|
+
})
|
|
210
292
|
|
|
211
|
-
|
|
212
|
-
|
|
293
|
+
// TODO: Set the timer to match the expiration of the transaction
|
|
294
|
+
const timer = setTimeout(() => {
|
|
295
|
+
prompt.cancel(
|
|
296
|
+
t('timeout', {default: 'The offer from the resource provider has expired.'})
|
|
297
|
+
)
|
|
298
|
+
}, 120000)
|
|
299
|
+
|
|
300
|
+
// Return the promise from the prompt
|
|
301
|
+
return prompt
|
|
302
|
+
.then(async () => {
|
|
303
|
+
// Return the modified transaction and additional signatures
|
|
304
|
+
return new Promise((r) =>
|
|
305
|
+
r({
|
|
306
|
+
request: modified,
|
|
307
|
+
signatures: json.data.signatures.map((sig) => Signature.from(sig)),
|
|
308
|
+
})
|
|
309
|
+
) as Promise<TransactHookResponse>
|
|
310
|
+
})
|
|
311
|
+
.catch((e) => {
|
|
312
|
+
// Throw if what we caught was a cancelation
|
|
313
|
+
if (e instanceof Canceled) {
|
|
314
|
+
throw e
|
|
315
|
+
}
|
|
316
|
+
// Otherwise if it wasn't a cancel, it was a reject, and continue without modification
|
|
317
|
+
return new Promise((r) => r({request})) as Promise<TransactHookResponse>
|
|
318
|
+
})
|
|
319
|
+
.finally(() => {
|
|
320
|
+
clearTimeout(timer) // TODO: Remove this, it's just here for testing
|
|
321
|
+
})
|
|
322
|
+
}
|
|
213
323
|
|
|
214
324
|
// Return the modified transaction and additional signatures
|
|
215
|
-
return
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
325
|
+
return new Promise((r) =>
|
|
326
|
+
r({
|
|
327
|
+
request: modified,
|
|
328
|
+
signatures: json.data.signatures.map((sig) => Signature.from(sig)),
|
|
329
|
+
})
|
|
330
|
+
)
|
|
219
331
|
}
|
|
220
332
|
|
|
221
333
|
getModifiedTransaction(json): Transaction {
|
|
@@ -235,10 +347,7 @@ export default class ResourceProviderPlugin extends AbstractTransactPlugin {
|
|
|
235
347
|
context: TransactContext
|
|
236
348
|
): Promise<SigningRequest> {
|
|
237
349
|
// Create a new signing request based on the response to return to the session's transact flow.
|
|
238
|
-
const request = await
|
|
239
|
-
{transaction: response.data.request[1]},
|
|
240
|
-
context.esrOptions
|
|
241
|
-
)
|
|
350
|
+
const request = await context.createRequest(response.data.request[1])
|
|
242
351
|
|
|
243
352
|
// Set the required fee onto the request itself for wallets to process.
|
|
244
353
|
if (response.code === 402 && response.data.fee) {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ko": {
|
|
3
|
+
"timeout": "리소스 공급자의 제안이 만료되었습니다.",
|
|
4
|
+
"will-continue": "트랜잭션은 리소스 공급자 없이 계속됩니다.",
|
|
5
|
+
"fee": {
|
|
6
|
+
"title": "거래 수수료를 수락하시겠습니까?",
|
|
7
|
+
"body": "계정에서 이 거래를 수행하려면 추가 리소스({{resource}})가 필요합니다. 이러한 리소스를 자동으로 구매하고 계속 진행하시겠습니까?",
|
|
8
|
+
"cost": "{{resource}}의 비용"
|
|
9
|
+
},
|
|
10
|
+
"rejected": {
|
|
11
|
+
"no-fees": "리소스 공급자가 이 트랜잭션을 유료로 처리하도록 제안했지만 수수료 기반 트랜잭션은 'allowFees = false'를 사용하는 구성에 의해 비활성화됩니다.",
|
|
12
|
+
"original-modified": "리소스 공급자가 반환한 원래 트랜잭션이 너무 많이 수정되었습니다. 리소스 공급자 없이 계속",
|
|
13
|
+
"max-fee": "리소스 공급자가 요청한 수수료가 비정상적으로 높아 거부되었습니다."
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"zh": {
|
|
17
|
+
"timeout": "来自资源提供程序的产品/服务已过期。",
|
|
18
|
+
"will-continue": "事务将在没有资源提供程序的情况下继续。",
|
|
19
|
+
"fee": {
|
|
20
|
+
"title": "接受交易费用?",
|
|
21
|
+
"body": "您的账户需要其他资源 ({{resource}}) 才能执行此事务。是否要自动购买这些资源并继续?",
|
|
22
|
+
"cost": "{{resource}} 的成本"
|
|
23
|
+
},
|
|
24
|
+
"rejected": {
|
|
25
|
+
"no-fees": "资源提供程序提供付费支付此事务的费用,但使用“allowFee = false”的配置禁用了基于费用的事务。",
|
|
26
|
+
"original-modified": "资源提供程序返回的原始事务修改过多。在没有资源提供程序的情况下继续",
|
|
27
|
+
"max-fee": "资源提供商请求的费用异常高,已被拒绝。"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @wharfkit/resource-provider-plugin v0.2.0
|
|
3
|
-
* https://github.com/wharfkit/resource-provider-plugin
|
|
4
|
-
*
|
|
5
|
-
* @license
|
|
6
|
-
* Copyright (c) 2021 FFF00 Agents AB & Greymass Inc. All Rights Reserved.
|
|
7
|
-
*
|
|
8
|
-
* Redistribution and use in source and binary forms, with or without modification,
|
|
9
|
-
* are permitted provided that the following conditions are met:
|
|
10
|
-
*
|
|
11
|
-
* 1. Redistribution of source code must retain the above copyright notice, this
|
|
12
|
-
* list of conditions and the following disclaimer.
|
|
13
|
-
*
|
|
14
|
-
* 2. Redistribution in binary form must reproduce the above copyright notice,
|
|
15
|
-
* this list of conditions and the following disclaimer in the documentation
|
|
16
|
-
* and/or other materials provided with the distribution.
|
|
17
|
-
*
|
|
18
|
-
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
19
|
-
* may be used to endorse or promote products derived from this software without
|
|
20
|
-
* specific prior written permission.
|
|
21
|
-
*
|
|
22
|
-
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
23
|
-
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
24
|
-
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
25
|
-
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
|
26
|
-
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
27
|
-
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
28
|
-
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
29
|
-
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
|
30
|
-
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
|
31
|
-
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
32
|
-
*
|
|
33
|
-
* YOU ACKNOWLEDGE THAT THIS SOFTWARE IS NOT DESIGNED, LICENSED OR INTENDED FOR USE
|
|
34
|
-
* IN THE DESIGN, CONSTRUCTION, OPERATION OR MAINTENANCE OF ANY MILITARY FACILITY.
|
|
35
|
-
*/
|
|
36
|
-
import { Struct, Name, Asset, AbstractTransactPlugin, TransactContext, ChainDefinition, SigningRequest, TransactHookResponse, Transaction, AssetType } from '@wharfkit/session';
|
|
37
|
-
|
|
38
|
-
interface ResourceProviderOptions {
|
|
39
|
-
allowFees?: boolean;
|
|
40
|
-
endpoints: Record<string, string>;
|
|
41
|
-
maxFee?: AssetType;
|
|
42
|
-
}
|
|
43
|
-
interface ResourceProviderResponseData {
|
|
44
|
-
request: [string, object];
|
|
45
|
-
signatures: string[];
|
|
46
|
-
version: unknown;
|
|
47
|
-
fee?: AssetType;
|
|
48
|
-
costs?: {
|
|
49
|
-
cpu: AssetType;
|
|
50
|
-
net: AssetType;
|
|
51
|
-
ram: AssetType;
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
interface ResourceProviderResponse {
|
|
55
|
-
code: number;
|
|
56
|
-
data: ResourceProviderResponseData;
|
|
57
|
-
}
|
|
58
|
-
declare class Transfer extends Struct {
|
|
59
|
-
from: Name;
|
|
60
|
-
to: Name;
|
|
61
|
-
quantity: Asset;
|
|
62
|
-
memo: string;
|
|
63
|
-
}
|
|
64
|
-
declare class ResourceProviderPlugin extends AbstractTransactPlugin {
|
|
65
|
-
readonly allowFees: boolean;
|
|
66
|
-
readonly maxFee?: Asset;
|
|
67
|
-
readonly endpoints: Record<string, string>;
|
|
68
|
-
constructor(options: ResourceProviderOptions);
|
|
69
|
-
register(context: TransactContext): void;
|
|
70
|
-
getEndpoint(chain: ChainDefinition): string;
|
|
71
|
-
request(request: SigningRequest, context: TransactContext): Promise<TransactHookResponse>;
|
|
72
|
-
getModifiedTransaction(json: any): Transaction;
|
|
73
|
-
createRequest(response: ResourceProviderResponse, context: TransactContext): Promise<SigningRequest>;
|
|
74
|
-
/**
|
|
75
|
-
* Perform validation against the request to ensure it is valid for the resource provider.
|
|
76
|
-
*/
|
|
77
|
-
validateRequest(request: SigningRequest, context: TransactContext): void;
|
|
78
|
-
/**
|
|
79
|
-
* Perform validation against the response to ensure it is valid for the session.
|
|
80
|
-
*/
|
|
81
|
-
validateResponseData(response: Record<string, any>): Promise<void>;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export { Transfer, ResourceProviderPlugin as default };
|