diff --git a/src/cli/cli.ts b/src/cli/cli.ts index b7d4c0d0..f661494b 100644 --- a/src/cli/cli.ts +++ b/src/cli/cli.ts @@ -23,6 +23,7 @@ import { mIOToken } from '../types/token.js'; import { version } from '../version.js'; import { joinNetwork } from './commands/joinNetwork.js'; import { transfer } from './commands/transfer.js'; +import { updateGatewaySettings } from './commands/updateGatewaySettings.js'; import { addressOptions, arNSAuctionPricesOptions, @@ -46,11 +47,9 @@ import { GetTokenCostOptions, GetVaultOptions, InitiatorOptions, - JoinNetworkOptions, NameOptions, PaginationAddressOptions, PaginationOptions, - TransferOptions, } from './types.js'; import { addressFromOptions, @@ -535,18 +534,25 @@ makeCommand({ }, }); -makeCommand({ +makeCommand({ name: 'transfer', description: 'Transfer IO to another address', options: transferOptions, - action: (o) => transfer(o), + action: transfer, }); -makeCommand({ +makeCommand({ name: 'join-network', - description: 'Join the AR.IO network', + description: 'Join a gateway to the AR.IO network', options: joinNetworkOptions, - action: (options) => joinNetwork(options), + action: joinNetwork, +}); + +makeCommand({ + name: 'update-gateway-settings', + description: 'Update AR.IO gateway settings', + options: joinNetworkOptions, + action: updateGatewaySettings, }); // delegate-stake @@ -557,8 +563,6 @@ makeCommand({ // withdraw-stake -// update-gateway-settings - // redelegate-stake if ( diff --git a/src/cli/commands/joinNetwork.ts b/src/cli/commands/joinNetwork.ts index 85cea60c..1608f3db 100644 --- a/src/cli/commands/joinNetwork.ts +++ b/src/cli/commands/joinNetwork.ts @@ -19,6 +19,7 @@ import { ArweaveSigner, IOToken, mIOToken } from '../../node/index.js'; import { JoinNetworkOptions } from '../types.js'; import { formatIOWithCommas, + gatewaySettingsFromOptions, jwkToAddress, requiredJwkFromOptions, writeIOFromOptions, @@ -30,60 +31,29 @@ export async function joinNetwork(options: JoinNetworkOptions) { const address = jwkToAddress(jwk); const io = writeIOFromOptions(options, new ArweaveSigner(jwk)); - const { - disableDelegatedStaking, - disableAutoStake, - delegateRewardShareRatio, - fqdn, - label, - minDelegatedStake, - note, - observerAddress, - port, - properties, - operatorStake, - allowedDelegates, - } = options; - - if (label === undefined) { - throw new Error( - 'Label is required. Please provide a --label for your node.', - ); - } - if (fqdn === undefined) { - throw new Error('FQDN is required. Please provide a --fqdn for your node.'); - } - if (operatorStake === undefined) { + if (options.operatorStake === undefined) { throw new Error( 'Operator stake is required. Please provide a --operator-stake denominated in IO for your node.', ); } - const ioQuantity = new IOToken(+operatorStake); + const ioQuantity = new IOToken(+options.operatorStake); const mIOOperatorStake = ioQuantity.toMIO().valueOf(); const settings = { - observerAddress, + ...gatewaySettingsFromOptions(options), operatorStake: mIOOperatorStake, - allowDelegatedStaking: - disableDelegatedStaking === undefined - ? undefined - : !disableDelegatedStaking, - autoStake: disableAutoStake === undefined ? undefined : !disableAutoStake, - delegateRewardShareRatio: - delegateRewardShareRatio !== undefined - ? +delegateRewardShareRatio - : undefined, - allowedDelegates, - fqdn, - label, - minDelegatedStake: - minDelegatedStake !== undefined ? +minDelegatedStake : undefined, - note, - port: port !== undefined ? +port : undefined, - properties, }; + if (settings.label === undefined) { + throw new Error( + 'Label is required. Please provide a --label for your node.', + ); + } + if (settings.fqdn === undefined) { + throw new Error('FQDN is required. Please provide a --fqdn for your node.'); + } + if (!options.skipConfirmation) { const balance = await io.getBalance({ address }); diff --git a/src/cli/commands/updateGatewaySettings.ts b/src/cli/commands/updateGatewaySettings.ts new file mode 100644 index 00000000..8f35be93 --- /dev/null +++ b/src/cli/commands/updateGatewaySettings.ts @@ -0,0 +1,66 @@ +/** + * 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 prompts from 'prompts'; + +import { ArweaveSigner } from '../../node/index.js'; +import { UpdateGatewaySettingsOptions } from '../types.js'; +import { + gatewaySettingsFromOptions, + jwkToAddress, + requiredJwkFromOptions, + writeIOFromOptions, + writeOptionsFromOptions, +} from '../utils.js'; + +export async function updateGatewaySettings( + options: UpdateGatewaySettingsOptions, +) { + const jwk = requiredJwkFromOptions(options); + const address = jwkToAddress(jwk); + const io = writeIOFromOptions(options, new ArweaveSigner(jwk)); + const gatewaySettings = gatewaySettingsFromOptions(options); + + if (Object.keys(gatewaySettings).length === 0) { + // TODO: The contract accepts empty Update-Gateway-Settings actions, but we'll throw in the CLI for now + throw new Error('No gateway settings provided'); + } + + if (!options.skipConfirmation) { + const { confirm } = await prompts({ + type: 'confirm', + name: 'confirm', + message: `Gateway Settings:\n\n${JSON.stringify(gatewaySettings, null, 2)}\n\nYou are about to update your gateway settings to the above\nAre you sure?\n`, + initial: true, + }); + + if (!confirm) { + return { message: 'Aborted update-gateway-settings command by user' }; + } + } + + const result = await io.updateGatewaySettings( + gatewaySettings, + writeOptionsFromOptions(options), + ); + + const output = { + updateGatewaySettingsResult: result, + updatedGatewayAddress: address, + message: `Gateway settings updated successfully`, + }; + + return output; +} diff --git a/src/cli/options.ts b/src/cli/options.ts index 9213fc02..4bb39db5 100644 --- a/src/cli/options.ts +++ b/src/cli/options.ts @@ -102,7 +102,7 @@ export const optionMap = { allowedDelegates: { alias: '--allowed-delegates ', description: - 'The allowed delegates for the gateway. By default this is empty, meaning all are allowed delegate stake', + 'The allowed delegates for the gateway. By default this is empty, meaning all are allowed delegate stake unless delegating is explicitly disallowed by the gateway', type: 'array', }, skipConfirmation: { @@ -242,11 +242,11 @@ export const transferOptions = [ optionMap.target, ]; -export const joinNetworkOptions = [ +export const updateGatewaySettingsOptions = [ ...writeActionOptions, - optionMap.operatorStake, optionMap.disableAutoStake, optionMap.disableDelegatedStaking, + optionMap.allowedDelegates, optionMap.minDelegatedStake, optionMap.delegateRewardShareRatio, optionMap.label, @@ -257,3 +257,8 @@ export const joinNetworkOptions = [ optionMap.port, optionMap.protocol, ]; + +export const joinNetworkOptions = [ + ...updateGatewaySettingsOptions, + optionMap.operatorStake, +]; diff --git a/src/cli/types.ts b/src/cli/types.ts index 3ca6fe34..7172fb89 100644 --- a/src/cli/types.ts +++ b/src/cli/types.ts @@ -109,6 +109,11 @@ export type JoinNetworkOptions = WriteActionOptions & skipConfirmation?: boolean; }; +export type UpdateGatewaySettingsOptions = Omit< + JoinNetworkOptions, + 'operatorStake' +>; + export type JsonSerializable = | string | number diff --git a/src/cli/utils.ts b/src/cli/utils.ts index 5f5b8b14..561c860d 100644 --- a/src/cli/utils.ts +++ b/src/cli/utils.ts @@ -20,6 +20,7 @@ import { readFileSync } from 'fs'; import { AoIORead, AoIOWrite, + AoUpdateGatewaySettingsParams, ArweaveSigner, ContractSigner, EpochInput, @@ -41,6 +42,7 @@ import { JsonSerializable, NameOptions, PaginationOptions, + UpdateGatewaySettingsOptions, VaultIdOptions, WalletOptions, WriteActionOptions, @@ -290,3 +292,38 @@ export function writeOptionsFromOptions( tags, }; } + +export function gatewaySettingsFromOptions({ + disableDelegatedStaking, + disableAutoStake, + delegateRewardShareRatio, + fqdn, + label, + minDelegatedStake, + note, + observerAddress, + port, + properties, + allowedDelegates, +}: UpdateGatewaySettingsOptions): AoUpdateGatewaySettingsParams { + return { + observerAddress, + allowDelegatedStaking: + disableDelegatedStaking === undefined + ? undefined + : !disableDelegatedStaking, + autoStake: disableAutoStake === undefined ? undefined : !disableAutoStake, + delegateRewardShareRatio: + delegateRewardShareRatio !== undefined + ? +delegateRewardShareRatio + : undefined, + allowedDelegates, + fqdn, + label, + minDelegatedStake: + minDelegatedStake !== undefined ? +minDelegatedStake : undefined, + note, + port: port !== undefined ? +port : undefined, + properties, + }; +} diff --git a/src/types/io.ts b/src/types/io.ts index 467ca1a0..520bbb6d 100644 --- a/src/types/io.ts +++ b/src/types/io.ts @@ -318,7 +318,9 @@ export type AoJoinNetworkParams = Pick & observerAddress?: WalletAddress; }; -export type AoUpdateGatewaySettingsParams = AtLeastOne; +export type AoUpdateGatewaySettingsParams = AtLeastOne< + Omit +>; export type AoGetArNSNameParams = { name: string;