Skip to content

Commit

Permalink
revert BaseCommand, ViemCommand now directly extends BaseCommand, rev…
Browse files Browse the repository at this point in the history
…ert test changes, remove CeloCommand
  • Loading branch information
shazarre committed Oct 16, 2024
1 parent 61138da commit b3940f2
Show file tree
Hide file tree
Showing 8 changed files with 247 additions and 272 deletions.
209 changes: 160 additions & 49 deletions packages/cli/src/base.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,107 @@
import { FeeCurrencyDirectory } from '@celo/abis-12/web3/FeeCurrencyDirectory'
import { FeeCurrencyWhitelist } from '@celo/abis/web3/FeeCurrencyWhitelist'
import { StrongAddress } from '@celo/base'
import { ReadOnlyWallet, isCel2 } from '@celo/connect'
import { ContractKit, newKitFromWeb3 } from '@celo/contractkit'
import {
AbstractFeeCurrencyWrapper,
FeeCurrencyInformation,
} from '@celo/contractkit/lib/wrappers/AbstractFeeCurrencyWrapper'
import { AzureHSMWallet } from '@celo/wallet-hsm-azure'
import { AddressValidation, newLedgerWalletWithSetup } from '@celo/wallet-ledger'
import { LocalWallet } from '@celo/wallet-local'
import _TransportNodeHid from '@ledgerhq/hw-transport-node-hid'
import { Command, Flags } from '@oclif/core'
import { CLIError } from '@oclif/core/lib/errors'
import chalk from 'chalk'
import net from 'net'
import Web3 from 'web3'
import { CeloCommand } from './celo'
import { CustomFlags } from './utils/command'
import { getNodeUrl } from './utils/config'
import { getFeeCurrencyContractWrapper } from './utils/fee-currency'
import { nodeIsSynced } from './utils/helpers'
import { requireNodeIsSynced } from './utils/helpers'

export abstract class BaseCommand extends Command {
static flags = {
privateKey: Flags.string({
char: 'k',
description: 'Use a private key to sign local transactions with',
hidden: true,
}),
node: Flags.string({
char: 'n',
description: "URL of the node to run commands against (defaults to 'http://localhost:8545')",
hidden: true,
parse: async (nodeUrl: string) => {
switch (nodeUrl) {
case 'local':
case 'localhost':
return 'http://localhost:8545'
case 'baklava':
return 'https://baklava-forno.celo-testnet.org'
case 'alfajores':
return 'https://alfajores-forno.celo-testnet.org'
case 'mainnet':
case 'forno':
return 'https://forno.celo.org'
default:
return nodeUrl
}
},
}),
gasCurrency: CustomFlags.gasCurrency({
description:
'Use a specific gas currency for transaction fees (defaults to CELO if no gas currency is supplied). It must be a whitelisted token.',
}),
useLedger: Flags.boolean({
default: false,
hidden: true,
description: 'Set it to use a ledger wallet',
}),
ledgerAddresses: Flags.integer({
default: 1,
hidden: true,
exclusive: ['ledgerCustomAddresses'],
description: 'If --useLedger is set, this will get the first N addresses for local signing',
}),
ledgerCustomAddresses: Flags.string({
default: '[0]',
hidden: true,
exclusive: ['ledgerAddresses'],
description:
'If --useLedger is set, this will get the array of index addresses for local signing. Example --ledgerCustomAddresses "[4,99]"',
}),
useAKV: Flags.boolean({
default: false,
hidden: true,
description: 'Set it to use an Azure KeyVault HSM',
}),
azureVaultName: Flags.string({
hidden: true,
description: 'If --useAKV is set, this is used to connect to the Azure KeyVault',
}),
ledgerConfirmAddress: Flags.boolean({
default: false,
hidden: true,
description: 'Set it to ask confirmation for the address of the transaction from the ledger',
}),
globalHelp: Flags.boolean({
default: false,
hidden: false,
description: 'View all available global flags',
}),
}
// This specifies whether the node needs to be synced before the command
// can be run. In most cases, this should be `true`, so that's the default.
// For commands that don't require the node is synced, add the following line
// to its definition:
// requireSynced = false
public requireSynced = true

// For the ease of migration BaseCommand is web3+CK
export abstract class BaseCommand extends CeloCommand {
private _web3: Web3 | null = null
private _kit: ContractKit | null = null
private feeCurrencyContractWrapper: AbstractFeeCurrencyWrapper<
FeeCurrencyWhitelist | FeeCurrencyDirectory
> | null = null

// Indicates if celocli running in L2 context
private cel2: boolean | null = null

async getWeb3() {
if (!this._web3) {
const nodeUrl = await this.getNodeUrl()
const res = await this.parse()
const nodeUrl = (res.flags && res.flags.node) || getNodeUrl(this.config.configDir)
this._web3 =
nodeUrl && nodeUrl.endsWith('.ipc')
? new Web3(new Web3.providers.IpcProvider(nodeUrl, net))
Expand All @@ -44,8 +119,8 @@ export abstract class BaseCommand extends CeloCommand {
}

async newWeb3() {
const nodeUrl = await this.getNodeUrl()

const res = await this.parse()
const nodeUrl = (res.flags && res.flags.node) || getNodeUrl(this.config.configDir)
return nodeUrl && nodeUrl.endsWith('.ipc')
? new Web3(new Web3.providers.IpcProvider(nodeUrl, net))
: new Web3(nodeUrl)
Expand All @@ -65,10 +140,19 @@ export abstract class BaseCommand extends CeloCommand {
}

async init() {
super.init()

if (this.requireSynced) {
const web3 = await this.getWeb3()
await requireNodeIsSynced(web3)
}
const kit = await this.getKit()
const res = await this.parse()
if (res.flags.globalHelp) {
console.log(chalk.red.bold('GLOBAL OPTIONS'))
Object.entries(BaseCommand.flags).forEach(([name, flag]) => {
console.log(chalk.black(` --${name}`).padEnd(40) + chalk.gray(`${flag.description}`))
})
process.exit(0)
}

if (res.flags.useLedger) {
try {
Expand Down Expand Up @@ -99,57 +183,84 @@ export abstract class BaseCommand extends CeloCommand {
console.log('Check if the ledger is connected and logged.')
throw err
}
} else if (res.flags.useAKV) {
try {
const akvWallet = new AzureHSMWallet(res.flags.azureVaultName)
await akvWallet.init()
console.log(`Found addresses: ${akvWallet.getAccounts()}`)
this._wallet = akvWallet
} catch (err) {
console.log(`Failed to connect to AKV ${err}`)
throw err
}
} else {
this._wallet = new LocalWallet()
}

if (res.flags.from) {
kit.defaultAccount = res.flags.from
}

const gasCurrencyFlag = res.flags.gasCurrency as StrongAddress | undefined

if (gasCurrencyFlag) {
const feeCurrencyContract = await getFeeCurrencyContractWrapper(kit, await this.isCel2())
const validFeeCurrencies = await feeCurrencyContract.getAddresses()

if (
validFeeCurrencies.map((x) => x.toLocaleLowerCase()).includes(gasCurrencyFlag.toLowerCase())
) {
kit.setFeeCurrency(gasCurrencyFlag)
} else {
const pairs = (
await feeCurrencyContract.getFeeCurrencyInformation(validFeeCurrencies as StrongAddress[])
).map(
({ name, symbol, address, adaptedToken }) =>
`${address} - ${name || 'unknown name'} (${symbol || 'N/A'})${
adaptedToken ? ` (adapted token: ${adaptedToken})` : ''
}`
)

throw new Error(
`${gasCurrencyFlag} is not a valid fee currency. Available currencies:\n${pairs.join(
'\n'
)}`
)
}
}
}

async finally(arg?: Error): Promise<any> {
async finally(arg: Error | undefined): Promise<any> {
try {
if (arg) {
if (!(arg instanceof CLIError)) {
console.error(
`
Received an error during command execution, if you believe this is a bug you can create an issue here:
https://github.com/celo-org/developer-tooling/issues/new?assignees=&labels=bug+report&projects=&template=BUG-FORM.yml
`,
arg
)
}
}

if (this._kit !== null) {
this._kit.connection.stop()
}
} catch (error) {
this.log(`Failed to close the connection: ${error}`)
}

super.finally(arg)
}

protected async checkIfL2(): Promise<boolean> {
return await isCel2(await this.getWeb3())
return super.finally(arg)
}

protected async getFeeCurrencyContractWrapper() {
if (!this.feeCurrencyContractWrapper) {
this.feeCurrencyContractWrapper = await getFeeCurrencyContractWrapper(
await this.getKit(),
await this.isCel2()
)
protected async isCel2(): Promise<boolean> {
if (this.cel2 === null) {
this.cel2 = await isCel2(await this.getWeb3())
}

return this.feeCurrencyContractWrapper
}

protected async getFeeCurrencyInformation(
addresses: StrongAddress[]
): Promise<FeeCurrencyInformation[]> {
const feeCurrencyContractWrapper = await this.getFeeCurrencyContractWrapper()

return await feeCurrencyContractWrapper.getFeeCurrencyInformation(addresses)
}

protected async getSupportedFeeCurrencyAddresses(): Promise<StrongAddress[]> {
const feeCurrencyContractWrapper = await this.getFeeCurrencyContractWrapper()

return await feeCurrencyContractWrapper.getAddresses()
}

protected async checkIfSynced() {
return await nodeIsSynced(await this.getWeb3())
return !!this.cel2
}
}
Loading

0 comments on commit b3940f2

Please sign in to comment.