From a9faad7ae95b5b84dc99ad90c3fb3739a09d7276 Mon Sep 17 00:00:00 2001 From: kaladinlight <35275952+kaladinlight@users.noreply.github.com> Date: Mon, 22 Jul 2024 12:17:37 -0600 Subject: [PATCH 1/2] feat: add metadata update flow and final tweaks --- cli/src/index.ts | 62 ++++++++++++++----- cli/src/ipfs.ts | 155 ++++++++++++++++++++++++++++++++++++++++++---- cli/src/types.ts | 2 + cli/src/wallet.ts | 18 +++--- 4 files changed, 201 insertions(+), 36 deletions(-) diff --git a/cli/src/index.ts b/cli/src/index.ts index dc28d7d..594aa9a 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -10,7 +10,7 @@ import { isEpochDistributionStarted } from './file' import { IPFS } from './ipfs' import { error, info, success, warn } from './logging' import { create, recoverKeystore } from './mnemonic' -import { Epoch } from './types' +import { EpochWithHash } from './types' import { Wallet } from './wallet' const processEpoch = async () => { @@ -21,7 +21,7 @@ const processEpoch = async () => { const month = MONTHS[new Date(metadata.epochStartTimestamp).getUTCMonth()] - info(`Processing Epoch #${metadata.epoch} for ${month} distribution.`) + info(`Processing rFOX Epoch #${metadata.epoch} for ${month} distribution.`) const now = Date.now() if (metadata.epochEndTimestamp > now) { @@ -88,7 +88,7 @@ const processEpoch = async () => { const nextEpochStartDate = new Date(metadata.epochEndTimestamp + 1) - await ipfs.updateMetadata(metadata, { + const hash = await ipfs.updateMetadata(metadata, { epoch: { number: metadata.epoch, hash: epochHash }, metadata: { epoch: metadata.epoch + 1, @@ -97,6 +97,8 @@ const processEpoch = async () => { }, }) + if (!hash) return + success(`rFOX Epoch #${metadata.epoch} has been processed!`) info( @@ -107,7 +109,7 @@ const processEpoch = async () => { const run = async () => { const ipfs = await IPFS.new() - const epoch = await ipfs.getEpoch() + const epoch = await ipfs.getEpochFromMetadata() if (isEpochDistributionStarted(epoch.number)) { const confirmed = await prompts.confirm({ @@ -138,10 +140,12 @@ const run = async () => { await processDistribution(epoch, wallet, ipfs) } -const recover = async (epoch?: Epoch) => { +const recover = async (epoch?: EpochWithHash) => { const ipfs = await IPFS.new() - if (!epoch) epoch = await ipfs.getEpoch() + if (!epoch) { + epoch = await ipfs.getEpochFromMetadata() + } const keystoreFile = path.join(RFOX_DIR, `keystore_epoch-${epoch.number}.txt`) const mnemonic = await recoverKeystore(keystoreFile) @@ -151,14 +155,33 @@ const recover = async (epoch?: Epoch) => { await processDistribution(epoch, wallet, ipfs) } -const processDistribution = async (epoch: Epoch, wallet: Wallet, ipfs: IPFS) => { +const update = async () => { + const ipfs = await IPFS.new() + + const metadata = await ipfs.getMetadata('update') + const hash = await ipfs.updateMetadata(metadata) + + if (!hash) return + + success(`rFOX metadata has been updated!`) + + info( + 'Please update the rFOX Wiki (https://github.com/shapeshift/rFOX/wiki/rFOX-Metadata) and notify the DAO accordingly. Thanks!', + ) +} + +const processDistribution = async (epoch: EpochWithHash, wallet: Wallet, ipfs: IPFS) => { await wallet.fund(epoch) const processedEpoch = await wallet.distribute(epoch) const processedEpochHash = await ipfs.addEpoch(processedEpoch) - const metadata = await ipfs.getMetadata('update') + const metadata = await ipfs.getMetadata('process') - await ipfs.updateMetadata(metadata, { epoch: { number: processedEpoch.number, hash: processedEpochHash } }) + const hash = await ipfs.updateMetadata(metadata, { + epoch: { number: processedEpoch.number, hash: processedEpochHash }, + }) + + if (!hash) return success(`rFOX reward distribution for Epoch #${processedEpoch.number} has been completed!`) @@ -183,23 +206,28 @@ const main = async () => { } } - const choice = await prompts.select<'process' | 'run' | 'recover'>({ + const choice = await prompts.select<'process' | 'run' | 'recover' | 'update'>({ message: 'What do you want to do?', choices: [ { - name: 'Process rFox epoch', + name: 'Process rFOX epoch', value: 'process', - description: 'Start here to process a completed rFox epoch', + description: 'Start here to process an rFOX epoch.', }, { - name: 'Run rFox distribution', + name: 'Run rFOX distribution', value: 'run', - description: 'Start here to begin running a new rFox rewards distribution', + description: 'Start here to run an rFOX rewards distribution.', }, { - name: 'Recover rFox distribution', + name: 'Recover rFOX distribution', value: 'recover', - description: 'Start here to recover an in progress rFox rewards distribution', + description: 'Start here to recover an rFOX rewards distribution.', + }, + { + name: 'Update rFOX metadata', + value: 'update', + description: 'Start here to update an rFOX metadata.', }, ], }) @@ -211,6 +239,8 @@ const main = async () => { return run() case 'recover': return recover() + case 'update': + return update() default: error(`Invalid choice: ${choice}, exiting.`) process.exit(1) diff --git a/cli/src/ipfs.ts b/cli/src/ipfs.ts index 7463435..9c86991 100644 --- a/cli/src/ipfs.ts +++ b/cli/src/ipfs.ts @@ -3,7 +3,8 @@ import PinataClient from '@pinata/sdk' import axios from 'axios' import BigNumber from 'bignumber.js' import { error, info } from './logging' -import { Epoch, RFOXMetadata, RewardDistribution } from './types' +import { Epoch, EpochWithHash, RFOXMetadata, RewardDistribution } from './types' +import { MONTHS } from './constants' const PINATA_API_KEY = process.env['PINATA_API_KEY'] const PINATA_SECRET_API_KEY = process.env['PINATA_SECRET_API_KEY'] @@ -98,10 +99,12 @@ export class IPFS { } } - async getEpoch(): Promise { - const hash = await prompts.input({ - message: 'What is the IPFS hash for the rFOX reward distribution you want to process? ', - }) + async getEpoch(hash?: string): Promise { + if (!hash) { + hash = await prompts.input({ + message: 'What is the IPFS hash for the rFOX epoch you want to process? ', + }) + } try { const { data } = await axios.get(`${PINATA_GATEWAY_URL}/ipfs/${hash}`, { @@ -111,6 +114,7 @@ export class IPFS { }) if (isEpoch(data)) { + const month = MONTHS[new Date(data.startTimestamp).getUTCMonth()] const totalAddresses = Object.keys(data.distributionsByStakingAddress).length const totalRewards = Object.values(data.distributionsByStakingAddress) .reduce((prev, distribution) => { @@ -120,12 +124,12 @@ export class IPFS { .toFixed() info( - `Processing rFOX reward distribution for Epoch #${data.number}:\n - Total Rewards: ${totalRewards} RUNE\n - Total Addresses: ${totalAddresses}`, + `Running ${month} rFOX reward distribution for Epoch #${data.number}:\n - Total Rewards: ${totalRewards} RUNE\n - Total Addresses: ${totalAddresses}`, ) - return data + return { ...data, hash } } else { - error(`The contents of IPFS hash (${hash}) are not valid, exiting.`) + error(`The contents of IPFS hash (${hash}) are not valid epoch contents, exiting.`) process.exit(1) } } catch { @@ -173,9 +177,113 @@ export class IPFS { } } - // TODO: manual update walkthrough + const choice = await prompts.select<'distributionRate' | 'burnRate' | 'treasuryAddress'>({ + message: 'What do you want to update?', + choices: [ + { + name: 'Distribution Rate', + value: 'distributionRate', + description: + 'Update the current percentage of revenue (RUNE) earned by the treasury to be distributed as rewards.', + }, + { + name: 'Burn Rate', + value: 'burnRate', + description: + 'Update the current percentage of revenue (RUNE) earned by the treasury to be used to buy FOX and then burn it.', + }, + { + name: 'Treasury Address', + value: 'treasuryAddress', + description: 'Update the THORChain treasury address used to determine revenue earned by the DAO.', + }, + ], + }) + + switch (choice) { + case 'distributionRate': { + const distributionRate = parseFloat( + await prompts.input({ + message: `The distribution rate is currently set to ${metadata.distributionRate}, what do you want to update it to? `, + }), + ) - return + if (isNaN(distributionRate) || distributionRate < 0 || distributionRate > 1) { + error(`Invalid distribution rate, it must be a number between 0 and 1 (ex. 0.25).`) + return this.updateMetadata(metadata) + } + + metadata.distributionRate = distributionRate + + break + } + case 'burnRate': { + const burnRate = parseFloat( + await prompts.input({ + message: `The burn rate is currently set to ${metadata.burnRate}, what do you want to update it to? `, + }), + ) + + if (isNaN(burnRate) || burnRate < 0 || burnRate > 1) { + error(`Invalid burn rate, it must be a number between 0 and 1 (ex. 0.25).`) + return this.updateMetadata(metadata) + } + + metadata.burnRate = burnRate + + break + } + case 'treasuryAddress': { + const treasuryAddress = await prompts.input({ + message: `The treasury address is currently set to ${metadata.treasuryAddress}, what do you want to update it to? `, + }) + + if (!/^thor[a-z0-9]{39}$/.test(treasuryAddress)) { + error(`Invalid treasury address, please check your address and try again.`) + return this.updateMetadata(metadata) + } + + metadata.treasuryAddress = treasuryAddress + + break + } + default: + error(`Invalid choice: ${choice}, exiting.`) + process.exit(1) + } + + const confirmed = await prompts.confirm({ + message: `Do you want to update another value?`, + }) + + if (confirmed) { + return this.updateMetadata(metadata) + } else { + if (metadata.distributionRate + metadata.burnRate > 1) { + error( + `Invalid rates, the sum of the distribution rate and burn rate must be a number between 0 and 1 (ex. 0.5).`, + ) + return this.updateMetadata(metadata) + } + + info( + `The new metadata values will be:\n - Distribtution Rate: ${metadata.distributionRate}\n - Burn Rate: ${metadata.burnRate}\n - Treasury Address: ${metadata.treasuryAddress}`, + ) + + const confirmed = await prompts.confirm({ + message: `Do you want to update the metadata with the new values?`, + }) + + if (!confirmed) return + + const { IpfsHash } = await this.client.pinJSONToIPFS(metadata, { + pinataMetadata: { name: 'rFoxMetadata.json' }, + }) + + info(`rFOX Metadata IPFS hash: ${IpfsHash}`) + + return IpfsHash + } } async getMetadata(promptAction: string): Promise { @@ -192,11 +300,36 @@ export class IPFS { if (isMetadata(data)) return data - error(`The contents of IPFS hash (${hash}) are not valid, exiting.`) + error(`The contents of IPFS hash (${hash}) are not valid metadata contents, exiting.`) process.exit(1) } catch { error(`Failed to get content of IPFS hash (${hash}), exiting.`) process.exit(1) } } + + async getEpochFromMetadata(): Promise { + const metadata = await this.getMetadata('process') + + const hash = metadata.ipfsHashByEpoch[metadata.epoch - 1] + + if (!hash) { + error(`No IPFS hash found for epoch ${metadata.epoch - 1}, exiting.`) + process.exit(1) + } + + const epoch = await this.getEpoch(hash) + const month = MONTHS[new Date(epoch.startTimestamp).getUTCMonth()] + + switch (epoch.distributionStatus) { + case 'pending': + info(`Running ${month} rFOX reward distribution for Epoch #${epoch.number}.`) + break + case 'complete': + info(`The ${month} rFOX reward distribution for Epoch #${epoch.number} is already complete, exiting.`) + process.exit(0) + } + + return epoch + } } diff --git a/cli/src/types.ts b/cli/src/types.ts index 9ddcd72..45aecda 100644 --- a/cli/src/types.ts +++ b/cli/src/types.ts @@ -88,3 +88,5 @@ export type Epoch = { /** A record of staking address to reward distribution for this epoch */ distributionsByStakingAddress: Record } + +export type EpochWithHash = Epoch & { hash: string } diff --git a/cli/src/wallet.ts b/cli/src/wallet.ts index 6f1f4d8..6777a4d 100644 --- a/cli/src/wallet.ts +++ b/cli/src/wallet.ts @@ -8,7 +8,7 @@ import ora, { Ora } from 'ora' import { RFOX_DIR } from './constants' import { read, write } from './file' import { error, info, success } from './logging' -import { Epoch } from './types' +import { EpochWithHash } from './types' const BIP32_PATH = `m/44'/931'/0'/0/0` const SHAPESHIFT_MULTISIG_ADDRESS = 'thor1xmaggkcln5m5fnha2780xrdrulmplvfrz6wj3l' @@ -70,7 +70,7 @@ export class Wallet { } } - private async buildFundingTransaction(amount: string, epoch: number) { + private async buildFundingTransaction(amount: string, epoch: EpochWithHash) { const { address } = await this.getAddress() return { @@ -88,7 +88,7 @@ export class Wallet { ], }, ], - memo: `Fund rFOX rewards distribution - Epoch #${epoch}`, + memo: `Fund rFOX rewards distribution - Epoch #${epoch.number} (IPFS Hash: ${epoch.hash})`, timeout_height: '0', extension_options: [], non_critical_extension_options: [], @@ -106,7 +106,7 @@ export class Wallet { } } - async fund(epoch: Epoch) { + async fund(epoch: EpochWithHash) { const { address } = await this.getAddress() const distributions = Object.values(epoch.distributionsByStakingAddress) @@ -152,7 +152,7 @@ export class Wallet { if (await isFunded()) return - const unsignedTx = await this.buildFundingTransaction(totalAmount, epoch.number) + const unsignedTx = await this.buildFundingTransaction(totalAmount, epoch) const unsignedTxFile = path.join(RFOX_DIR, `unsignedTx_epoch-${epoch.number}.json`) write(unsignedTxFile, JSON.stringify(unsignedTx, null, 2)) @@ -173,7 +173,7 @@ export class Wallet { })() } - private async signTransactions(epoch: Epoch): Promise { + private async signTransactions(epoch: EpochWithHash): Promise { const txsFile = path.join(RFOX_DIR, `txs_epoch-${epoch.number}.json`) const txs = read(txsFile) @@ -230,7 +230,7 @@ export class Wallet { amount: [], gas: '0', }, - memo: `rFOX reward (Staking Address: ${stakingAddress}) - Epoch #${epoch.number}`, + memo: `rFOX reward (Staking Address: ${stakingAddress}) - Epoch #${epoch.number} (IPFS Hash: ${epoch.hash})`, signatures: [], }, } @@ -273,7 +273,7 @@ export class Wallet { return txsByStakingAddress } - async broadcastTransactions(epoch: Epoch, txsByStakingAddress: TxsByStakingAddress): Promise { + async broadcastTransactions(epoch: EpochWithHash, txsByStakingAddress: TxsByStakingAddress): Promise { const totalTxs = Object.values(epoch.distributionsByStakingAddress).length const spinner = ora(`Broadcasting ${totalTxs} transactions...`).start() @@ -330,7 +330,7 @@ export class Wallet { return epoch } - async distribute(epoch: Epoch): Promise { + async distribute(epoch: EpochWithHash): Promise { const txsByStakingAddress = await this.signTransactions(epoch) return this.broadcastTransactions(epoch, txsByStakingAddress) } From ab54bc5cf9c6dba670e17639297aa1c97354a297 Mon Sep 17 00:00:00 2001 From: kaladinlight <35275952+kaladinlight@users.noreply.github.com> Date: Tue, 23 Jul 2024 20:23:57 -0600 Subject: [PATCH 2/2] post multisig run through updates --- cli/MultiSig.md | 32 ++++++++++++++++++++++---------- cli/src/index.ts | 37 ++++++++++++++++++++++--------------- cli/src/ipfs.ts | 22 ++++------------------ cli/src/types.ts | 2 -- cli/src/wallet.ts | 20 ++++++++++---------- 5 files changed, 58 insertions(+), 55 deletions(-) diff --git a/cli/MultiSig.md b/cli/MultiSig.md index f7afa0f..ed816cf 100644 --- a/cli/MultiSig.md +++ b/cli/MultiSig.md @@ -1,12 +1,19 @@ ## Prerequisites -- Install golang: https://go.dev/doc/install +- Install golang (v1.22): https://go.dev/doc/install +- Create common rfox directory in your home directory. This is where all output files from the script will be stored and where shared files (unsigned transactions, signatures, signed transactions, etc.) should be saved. + ```bash + mkdir ~/rfox + ``` ## Clone and Build ```bash git clone https://gitlab.com/thorchain/thornode.git -cd thornode/cmd/thornode +cd thornode +git checkout develop +git pull +cd cmd/thornode go build --tags cgo,ledger ``` @@ -22,8 +29,12 @@ go build --tags cgo,ledger ``` - Import signer pubkeys: ```bash - ./thornode keys add {person2} --pubkey {pubkey} - ./thornode keys add {person3} --pubkey {pubkey} + ./thornode keys add {person2} --pubkey '{person2_pubkey}' + ./thornode keys add {person3} --pubkey '{person3_pubkey}' + ``` +- View keys: + ```bash + ./thornode keys list ``` - Add multisig key: ```bash @@ -38,15 +49,15 @@ go build --tags cgo,ledger - Person 1 signs: ```bash - ./thornode tx sign --from {person1} --multisig multisig {unsignedTx_epoch-N.json} --chain-id thorchain-mainnet-v1 --node https://daemon.thorchain.shapeshift.com:443/rpc --from ledger --ledger --sign-mode amino-json > signedTx_{person1}.json + ./thornode tx sign --from {person1} --multisig multisig ~/rfox/unsignedTx_epoch-{N}.json --chain-id thorchain-mainnet-v1 --node https://daemon.thorchain.shapeshift.com:443/rpc --ledger --sign-mode amino-json > ~/rfox/signedTx_epoch-{N}_{person1}.json ``` - Person 2 signs: ```bash - ./thornode tx sign --from {person2} --multisig multisig {unsignedTx_epoch-N.json} --chain-id thorchain-mainnet-v1 --node https://daemon.thorchain.shapeshift.com:443/rpc --from ledger --ledger --sign-mode amino-json > signedTx_{person2}.json + ./thornode tx sign --from {person2} --multisig multisig ~/rfox/unsignedTx_epoch-{N}.json --chain-id thorchain-mainnet-v1 --node https://daemon.thorchain.shapeshift.com:443/rpc --ledger --sign-mode amino-json > ~/rfox/signedTx_epoch-{N}_{person2}.json ``` - Multisign: ```bash - ./thornode tx multisign {unsignedTx_epoch-N.json} multisig signedTx_{person1}.json signedTx_{person2}.json --from multisig --chain-id thorchain-mainnet-v1 --node https://daemon.thorchain.shapeshift.com:443/rpc > signedTx_multisig.json + ./thornode tx multisign ~/rfox/unsignedTx_epoch-{N}.json multisig ~/rfox/signedTx_epoch-{N}_{person1}.json ~/rfox/signedTx_epoch-{N}_{person2}.json --from multisig --chain-id thorchain-mainnet-v1 --node https://daemon.thorchain.shapeshift.com:443/rpc > ~/rfox/signedTx_epoch-{N}_multisig.json ``` ## Send Transaction @@ -54,13 +65,14 @@ go build --tags cgo,ledger - Simulate transaction: ```bash - ./thornode tx broadcast signedTx_multisig.json --chain-id thorchain-mainnet-v1 --node https://daemon.thorchain.shapeshift.com:443/rpc --gas auto --dry-run > simulatedTx.json + ./thornode tx broadcast ~/rfox/signedTx_epoch-{N}_multisig.json --chain-id thorchain-mainnet-v1 --node https://daemon.thorchain.shapeshift.com:443/rpc --dry-run > ~/rfox/simulatedTx_epoch-{N}.json ``` - Validate contents of `simulatedTx.json` for accuracy before broadcasting - Broadcast transaction: ```bash - ./thornode tx broadcast signedTx_multisig.json --chain-id thorchain-mainnet-v1 --node https://daemon.thorchain.shapeshift.com:443/rpc --gas auto > tx.json + ./thornode tx broadcast ~/rfox/signedTx_epoch-{N}_multisig.json --chain-id thorchain-mainnet-v1 --node https://daemon.thorchain.shapeshift.com:443/rpc --gas auto > tx.json ``` - - Copy the `txhash` value from `tx.json` to supply to the cli in order to continue + +At this point, the cli should pick up the funding transaction and continue running the distribution from the hot wallet. diff --git a/cli/src/index.ts b/cli/src/index.ts index 594aa9a..e6bfde9 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -10,7 +10,7 @@ import { isEpochDistributionStarted } from './file' import { IPFS } from './ipfs' import { error, info, success, warn } from './logging' import { create, recoverKeystore } from './mnemonic' -import { EpochWithHash } from './types' +import { Epoch, RFOXMetadata } from './types' import { Wallet } from './wallet' const processEpoch = async () => { @@ -109,14 +109,15 @@ const processEpoch = async () => { const run = async () => { const ipfs = await IPFS.new() - const epoch = await ipfs.getEpochFromMetadata() + const metadata = await ipfs.getMetadata('process') + const epoch = await ipfs.getEpochFromMetadata(metadata) if (isEpochDistributionStarted(epoch.number)) { const confirmed = await prompts.confirm({ message: 'It looks like you have already started a distribution for this epoch. Do you want to continue? ', }) - if (confirmed) return recover(epoch) + if (confirmed) return recover(metadata) info(`Please move or delete all existing files for epoch-${epoch.number} from ${RFOX_DIR} before re-running.`) warn('This action should never be taken unless you are absolutely sure you know what you are doing!!!') @@ -137,22 +138,24 @@ const run = async () => { const wallet = await Wallet.new(mnemonic) - await processDistribution(epoch, wallet, ipfs) + await processDistribution(metadata, epoch, wallet, ipfs) } -const recover = async (epoch?: EpochWithHash) => { +const recover = async (metadata?: RFOXMetadata) => { const ipfs = await IPFS.new() - if (!epoch) { - epoch = await ipfs.getEpochFromMetadata() + if (!metadata) { + metadata = await ipfs.getMetadata('process') } + const epoch = await ipfs.getEpochFromMetadata(metadata) + const keystoreFile = path.join(RFOX_DIR, `keystore_epoch-${epoch.number}.txt`) const mnemonic = await recoverKeystore(keystoreFile) const wallet = await Wallet.new(mnemonic) - await processDistribution(epoch, wallet, ipfs) + await processDistribution(metadata, epoch, wallet, ipfs) } const update = async () => { @@ -170,18 +173,22 @@ const update = async () => { ) } -const processDistribution = async (epoch: EpochWithHash, wallet: Wallet, ipfs: IPFS) => { - await wallet.fund(epoch) - const processedEpoch = await wallet.distribute(epoch) +const processDistribution = async (metadata: RFOXMetadata, epoch: Epoch, wallet: Wallet, ipfs: IPFS) => { + const epochHash = metadata.ipfsHashByEpoch[epoch.number] - const processedEpochHash = await ipfs.addEpoch(processedEpoch) - const metadata = await ipfs.getMetadata('process') + await wallet.fund(epoch, epochHash) + const processedEpoch = await wallet.distribute(epoch, epochHash) - const hash = await ipfs.updateMetadata(metadata, { + const processedEpochHash = await ipfs.addEpoch({ + ...processedEpoch, + distributionStatus: 'complete', + }) + + const metadataHash = await ipfs.updateMetadata(metadata, { epoch: { number: processedEpoch.number, hash: processedEpochHash }, }) - if (!hash) return + if (!metadataHash) return success(`rFOX reward distribution for Epoch #${processedEpoch.number} has been completed!`) diff --git a/cli/src/ipfs.ts b/cli/src/ipfs.ts index 9c86991..e62890f 100644 --- a/cli/src/ipfs.ts +++ b/cli/src/ipfs.ts @@ -3,7 +3,7 @@ import PinataClient from '@pinata/sdk' import axios from 'axios' import BigNumber from 'bignumber.js' import { error, info } from './logging' -import { Epoch, EpochWithHash, RFOXMetadata, RewardDistribution } from './types' +import { Epoch, RFOXMetadata, RewardDistribution } from './types' import { MONTHS } from './constants' const PINATA_API_KEY = process.env['PINATA_API_KEY'] @@ -99,7 +99,7 @@ export class IPFS { } } - async getEpoch(hash?: string): Promise { + async getEpoch(hash?: string): Promise { if (!hash) { hash = await prompts.input({ message: 'What is the IPFS hash for the rFOX epoch you want to process? ', @@ -127,7 +127,7 @@ export class IPFS { `Running ${month} rFOX reward distribution for Epoch #${data.number}:\n - Total Rewards: ${totalRewards} RUNE\n - Total Addresses: ${totalAddresses}`, ) - return { ...data, hash } + return data } else { error(`The contents of IPFS hash (${hash}) are not valid epoch contents, exiting.`) process.exit(1) @@ -153,18 +153,6 @@ export class IPFS { metadata.epochEndTimestamp = overrides.metadata.epochEndTimestamp if (overrides.epoch) { - const hash = metadata.ipfsHashByEpoch[overrides.epoch.number] - - if (hash) { - info(`The metadata already contains an IPFS hash for this epoch: ${hash}`) - - const confirmed = await prompts.confirm({ - message: `Do you want to update the metadata with the new IPFS hash: ${overrides.epoch.hash}?`, - }) - - if (!confirmed) return - } - metadata.ipfsHashByEpoch[overrides.epoch.number] = overrides.epoch.hash const { IpfsHash } = await this.client.pinJSONToIPFS(metadata, { @@ -308,9 +296,7 @@ export class IPFS { } } - async getEpochFromMetadata(): Promise { - const metadata = await this.getMetadata('process') - + async getEpochFromMetadata(metadata: RFOXMetadata): Promise { const hash = metadata.ipfsHashByEpoch[metadata.epoch - 1] if (!hash) { diff --git a/cli/src/types.ts b/cli/src/types.ts index 45aecda..9ddcd72 100644 --- a/cli/src/types.ts +++ b/cli/src/types.ts @@ -88,5 +88,3 @@ export type Epoch = { /** A record of staking address to reward distribution for this epoch */ distributionsByStakingAddress: Record } - -export type EpochWithHash = Epoch & { hash: string } diff --git a/cli/src/wallet.ts b/cli/src/wallet.ts index 6777a4d..a04aea1 100644 --- a/cli/src/wallet.ts +++ b/cli/src/wallet.ts @@ -8,7 +8,7 @@ import ora, { Ora } from 'ora' import { RFOX_DIR } from './constants' import { read, write } from './file' import { error, info, success } from './logging' -import { EpochWithHash } from './types' +import { Epoch } from './types' const BIP32_PATH = `m/44'/931'/0'/0/0` const SHAPESHIFT_MULTISIG_ADDRESS = 'thor1xmaggkcln5m5fnha2780xrdrulmplvfrz6wj3l' @@ -70,7 +70,7 @@ export class Wallet { } } - private async buildFundingTransaction(amount: string, epoch: EpochWithHash) { + private async buildFundingTransaction(amount: string, epoch: Epoch, hash: string) { const { address } = await this.getAddress() return { @@ -88,7 +88,7 @@ export class Wallet { ], }, ], - memo: `Fund rFOX rewards distribution - Epoch #${epoch.number} (IPFS Hash: ${epoch.hash})`, + memo: `Fund rFOX rewards distribution - Epoch #${epoch.number} (IPFS Hash: ${hash})`, timeout_height: '0', extension_options: [], non_critical_extension_options: [], @@ -106,7 +106,7 @@ export class Wallet { } } - async fund(epoch: EpochWithHash) { + async fund(epoch: Epoch, epochHash: string) { const { address } = await this.getAddress() const distributions = Object.values(epoch.distributionsByStakingAddress) @@ -152,7 +152,7 @@ export class Wallet { if (await isFunded()) return - const unsignedTx = await this.buildFundingTransaction(totalAmount, epoch) + const unsignedTx = await this.buildFundingTransaction(totalAmount, epoch, epochHash) const unsignedTxFile = path.join(RFOX_DIR, `unsignedTx_epoch-${epoch.number}.json`) write(unsignedTxFile, JSON.stringify(unsignedTx, null, 2)) @@ -173,7 +173,7 @@ export class Wallet { })() } - private async signTransactions(epoch: EpochWithHash): Promise { + private async signTransactions(epoch: Epoch, epochHash: string): Promise { const txsFile = path.join(RFOX_DIR, `txs_epoch-${epoch.number}.json`) const txs = read(txsFile) @@ -230,7 +230,7 @@ export class Wallet { amount: [], gas: '0', }, - memo: `rFOX reward (Staking Address: ${stakingAddress}) - Epoch #${epoch.number} (IPFS Hash: ${epoch.hash})`, + memo: `rFOX reward (Staking Address: ${stakingAddress}) - Epoch #${epoch.number} (IPFS Hash: ${epochHash})`, signatures: [], }, } @@ -273,7 +273,7 @@ export class Wallet { return txsByStakingAddress } - async broadcastTransactions(epoch: EpochWithHash, txsByStakingAddress: TxsByStakingAddress): Promise { + async broadcastTransactions(epoch: Epoch, txsByStakingAddress: TxsByStakingAddress): Promise { const totalTxs = Object.values(epoch.distributionsByStakingAddress).length const spinner = ora(`Broadcasting ${totalTxs} transactions...`).start() @@ -330,8 +330,8 @@ export class Wallet { return epoch } - async distribute(epoch: EpochWithHash): Promise { - const txsByStakingAddress = await this.signTransactions(epoch) + async distribute(epoch: Epoch, epochHash: string): Promise { + const txsByStakingAddress = await this.signTransactions(epoch, epochHash) return this.broadcastTransactions(epoch, txsByStakingAddress) } }