@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/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: Record<string, string>
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 default class ResourceProviderPlugin extends AbstractTransactPlugin {
53
- readonly allowFees: boolean = false
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: ResourceProviderOptions) {
81
+ constructor(options?: ResourceProviderOptions) {
63
82
  super()
64
- // Set the endpoints and chains available
65
- this.endpoints = options.endpoints
66
- if (typeof options?.allowFees !== 'undefined') {
67
- this.allowFees = options.allowFees
68
- }
69
- // TODO: Allow contact/action combos to be passed in and checked against to ensure no rogue actions were appended.
70
- // if (typeof options.allowActions !== 'undefined') {
71
- // this.allowActions = options.allowActions.map((action) => Name.from(action))
72
- // }
73
- if (typeof options?.maxFee !== 'undefined') {
74
- this.maxFee = Asset.from(options.maxFee)
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(TransactHookTypes.beforeSign, (request, context) =>
80
- this.request(request, context)
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
- // NYI: Interact with interface via context for fee based prompting
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
- // If the user did not accept the fee, return the original request without modification.
204
- if (!promptResponse) {
205
- return {
206
- request,
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
- // Create a new signing request based on the response to return to the session's transact flow.
212
- const modified = await this.createRequest(json, context)
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
- request: modified,
217
- signatures: json.data.signatures.map((sig) => Signature.from(sig)),
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 SigningRequest.create(
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 };