From f2d26daa5ee12008dc8173a0ee44bfc2828028ba Mon Sep 17 00:00:00 2001 From: Derek Sonnenberg Date: Wed, 16 Oct 2024 15:50:05 -0500 Subject: [PATCH] feat(delegated payments): add revoke approvals to SDK and CLI PE-6754 --- src/cli/cli.ts | 12 ++++++++++ src/cli/commands/revokeApprovals.ts | 36 +++++++++++++++++++++++++++++ src/cli/options.ts | 2 ++ src/cli/types.ts | 4 ++++ src/common/turbo.ts | 14 +++++++++++ src/common/upload.ts | 21 +++++++++++++++++ src/types.ts | 7 ++++++ 7 files changed, 96 insertions(+) create mode 100644 src/cli/commands/revokeApprovals.ts diff --git a/src/cli/cli.ts b/src/cli/cli.ts index bf66424e..8df93bdd 100644 --- a/src/cli/cli.ts +++ b/src/cli/cli.ts @@ -29,10 +29,12 @@ import { uploadFile, uploadFolder, } from './commands/index.js'; +import { revokeApprovals } from './commands/revokeApprovals.js'; import { createApprovalOptions, globalOptions, optionMap, + revokeApprovalsOptions, uploadFileOptions, uploadFolderOptions, walletOptions, @@ -103,6 +105,16 @@ applyOptions( ).action(async (_commandOptions, command: Command) => { await runCommand(command, createApproval); }); +applyOptions( + program + .command('revoke-approvals') + .description( + 'Revokes all Turbo delegated payment approvals for given address', + ), + revokeApprovalsOptions, +).action(async (_commandOptions, command: Command) => { + await runCommand(command, revokeApprovals); +}); if ( process.argv[1].includes('bin/turbo') || // Running from global .bin diff --git a/src/cli/commands/revokeApprovals.ts b/src/cli/commands/revokeApprovals.ts new file mode 100644 index 00000000..1c0fe981 --- /dev/null +++ b/src/cli/commands/revokeApprovals.ts @@ -0,0 +1,36 @@ +/** + * Copyright (C) 2022-2024 Permanent Data Solutions, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { RevokeApprovalsOptions } from '../types.js'; +import { turboFromOptions } from '../utils.js'; + +export async function revokeApprovals( + options: RevokeApprovalsOptions, +): Promise { + const { address: revokedAddress } = options; + if (revokedAddress === undefined) { + throw new Error( + 'Must provide an approved --address to create approval for', + ); + } + + const turbo = await turboFromOptions(options); + + const result = await turbo.revokeDelegatedPaymentApprovals({ + revokedAddress, + }); + + console.log('Created approval:', JSON.stringify(result, null, 2)); +} diff --git a/src/cli/options.ts b/src/cli/options.ts index 9d50e2d5..4f739a8e 100644 --- a/src/cli/options.ts +++ b/src/cli/options.ts @@ -170,3 +170,5 @@ export const createApprovalOptions = [ optionMap.address, optionMap.expiresBySeconds, ]; + +export const revokeApprovalsOptions = [...walletOptions, optionMap.address]; diff --git a/src/cli/types.ts b/src/cli/types.ts index 7aca7060..8d6fa61b 100644 --- a/src/cli/types.ts +++ b/src/cli/types.ts @@ -68,3 +68,7 @@ export type CreateApprovalOptions = WalletOptions & { value: string | undefined; expiresBySeconds: number | undefined; }; + +export type RevokeApprovalsOptions = WalletOptions & { + address: string | undefined; +}; diff --git a/src/common/turbo.ts b/src/common/turbo.ts index fa507d1e..1e2d7ae5 100644 --- a/src/common/turbo.ts +++ b/src/common/turbo.ts @@ -35,6 +35,7 @@ import { TurboFundWithTokensParams, TurboPriceResponse, TurboRatesResponse, + TurboRevokeDelegatePaymentApprovalsParams, TurboSignedDataItemFactory, TurboSubmitFundTxResponse, TurboUnauthenticatedClientConfiguration, @@ -277,4 +278,17 @@ export class TurboAuthenticatedClient ): Promise { return this.uploadService.createDelegatedPaymentApproval(p); } + + /** + * Creates a data item with tags that designate it as a revoke action for delegated + * payment approvals for target revokedAddress. Signs the data item and sends it to + * the Turbo Upload Service, which will verify the signature and forward the admin + * action towards the Turbo Payment Service. + */ + + revokeDelegatedPaymentApprovals( + p: TurboRevokeDelegatePaymentApprovalsParams, + ): Promise { + return this.uploadService.revokeDelegatedPaymentApprovals(p); + } } diff --git a/src/common/upload.ts b/src/common/upload.ts index 12437fc8..56f864c4 100644 --- a/src/common/upload.ts +++ b/src/common/upload.ts @@ -28,6 +28,7 @@ import { TurboDataItemSigner, TurboFileFactory, TurboLogger, + TurboRevokeDelegatePaymentApprovalsParams, TurboSignedDataItemFactory, TurboUnauthenticatedUploadServiceConfiguration, TurboUnauthenticatedUploadServiceInterface, @@ -336,4 +337,24 @@ export abstract class TurboAuthenticatedBaseUploadService dataItemOpts, }); } + + public async revokeDelegatedPaymentApprovals({ + revokedAddress, + }: TurboRevokeDelegatePaymentApprovalsParams): Promise { + const dataItemOpts = { + tags: [ + { + name: revokeDelegatePaymentApprovalTagName, + value: revokedAddress, + }, + ], + }; + + const nonceData = Buffer.from(revokedAddress + Date.now()); + return this.uploadFile({ + fileStreamFactory: () => Readable.from(nonceData), + fileSizeFactory: () => nonceData.byteLength, + dataItemOpts, + }); + } } diff --git a/src/types.ts b/src/types.ts index b52bea6a..c7d15fc5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -213,6 +213,10 @@ export type TurboCreateDelegatedPaymentApprovalParams = { expiresBySeconds?: number; }; +export type TurboRevokeDelegatePaymentApprovalsParams = { + revokedAddress: string; +}; + export type TurboUploadFolderResponse = { fileResponses: TurboUploadDataItemResponse[]; manifestResponse?: TurboUploadDataItemResponse; @@ -586,6 +590,9 @@ export interface TurboAuthenticatedUploadServiceInterface createDelegatedPaymentApproval( p: TurboCreateDelegatedPaymentApprovalParams, ): Promise; + revokeDelegatedPaymentApprovals( + p: TurboRevokeDelegatePaymentApprovalsParams, + ): Promise; } export interface TurboUnauthenticatedClientInterface