Skip to content

Commit

Permalink
Various improvements to the CLI, allow voters to revote for a group (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Asa Oines authored and m-chrzan committed Nov 25, 2019
1 parent 51f61b6 commit a3fdf21
Show file tree
Hide file tree
Showing 34 changed files with 494 additions and 186 deletions.
4 changes: 2 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ jobs:
name: Run test itself

command: |
cd ~/src/packages/mobile
cd ~/src/packages/mobile
# detox sometimes without releasing the terminal and thus making the CI timout
# 480s = 8 minutes
timeout 480 yarn test:detox || echo "failed, try again"
Expand Down Expand Up @@ -402,7 +402,7 @@ jobs:
- run:
name: Generate DevChain
command: |
(cd packages/contractkit && yarn test:prepare)
(cd packages/contractkit && yarn test:reset)
- run:
name: Run Tests
command: yarn --cwd=packages/contractkit test
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"docs": "yarn oclif-dev readme --multi --dir=../docs/command-line-interface && yarn prettier ../docs/command-line-interface/*.md --write",
"lint": "tslint -c tslint.json --project tsconfig.json",
"prepack": "yarn run build && oclif-dev manifest && oclif-dev readme",
"test:reset": "yarn --cwd ../protocol devchain generate .devchain",
"test:reset": "yarn --cwd ../protocol devchain generate .devchain --migration_override ../dev-utils/src/migration-override.json",
"test:livechain": "yarn --cwd ../protocol devchain run .devchain",
"test": "TZ=UTC jest --runInBand"
},
Expand Down
34 changes: 4 additions & 30 deletions packages/cli/src/commands/account/transferdollar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,50 +11,24 @@ export default class DollarTransfer extends BaseCommand {
...BaseCommand.flags,
from: Flags.address({ required: true, description: 'Address of the sender' }),
to: Flags.address({ required: true, description: 'Address of the receiver' }),
amountInWei: flags.string({ required: true, description: 'Amount to transfer (in wei)' }),
value: flags.string({ required: true, description: 'Amount to transfer (in wei)' }),
}

static examples = [
'transferdollar --from 0xa0Af2E71cECc248f4a7fD606F203467B500Dd53B --to 0x5409ed021d9299bf6814279a6a1411a7e866a631 --amountInWei 1',
'transferdollar --from 0xa0Af2E71cECc248f4a7fD606F203467B500Dd53B --to 0x5409ed021d9299bf6814279a6a1411a7e866a631 --value 1',
]

async run() {
const res = this.parse(DollarTransfer)

const from: string = res.flags.from
const to: string = res.flags.to
const amountInWei = new BigNumber(res.flags.amountInWei)
const value = new BigNumber(res.flags.value)

this.kit.defaultAccount = from

const goldToken = await this.kit.contracts.getGoldToken()
const stableToken = await this.kit.contracts.getStableToken()
// Units of all balances are in wei, unless specified.
// Check the balance before
const goldBalanceFromBefore = await goldToken.balanceOf(from)
const dollarBalanceFromBefore = await stableToken.balanceOf(from)

// Perform the transfer
await displaySendTx('dollar.Transfer', stableToken.transfer(to, amountInWei.toString()))

// Check the balance after
const goldBalanceFromAfter = await goldToken.balanceOf(from)
const dollarBalanceFromAfter = await stableToken.balanceOf(from)

// Get gas cost
const goldDifference = goldBalanceFromBefore.minus(goldBalanceFromAfter)
const dollarDifference = dollarBalanceFromBefore.minus(dollarBalanceFromAfter)
const gasCostInWei = goldDifference
this.log(
`Transferred ${amountInWei} from ${from} to ${to}, gas cost: ${gasCostInWei.toString()}`
)
this.log(
`Dollar Balance of sender ${from} went down by ${dollarDifference.toString()} wei,` +
`final balance: ${dollarBalanceFromAfter.toString()} Celo Dollars wei`
)
this.log(
`Gold Balance of sender ${from} went down by ${goldDifference.toString()} wei, ` +
`final balance: ${goldBalanceFromAfter.toString()} Celo Gold wei`
)
await displaySendTx('dollar.Transfer', stableToken.transfer(to, value.toFixed()))
}
}
26 changes: 4 additions & 22 deletions packages/cli/src/commands/account/transfergold.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,42 +11,24 @@ export default class GoldTransfer extends BaseCommand {
...BaseCommand.flags,
from: Flags.address({ required: true, description: 'Address of the sender' }),
to: Flags.address({ required: true, description: 'Address of the receiver' }),
amountInWei: flags.string({ required: true, description: 'Amount to transfer (in wei)' }),
value: flags.string({ required: true, description: 'Amount to transfer (in wei)' }),
}

static examples = [
'transfergold --from 0xa0Af2E71cECc248f4a7fD606F203467B500Dd53B --to 0x5409ed021d9299bf6814279a6a1411a7e866a631 --amountInWei 1',
'transfergold --from 0xa0Af2E71cECc248f4a7fD606F203467B500Dd53B --to 0x5409ed021d9299bf6814279a6a1411a7e866a631 --value 1',
]

async run() {
const res = this.parse(GoldTransfer)

const from: string = res.flags.from
const to: string = res.flags.to
const amountInWei = new BigNumber(res.flags.amountInWei)
const value = new BigNumber(res.flags.value)

this.kit.defaultAccount = from
// Units of all balances are in wei, unless specified.
// Check the balance before
const goldToken = await this.kit.contracts.getGoldToken()

// Check the balance before
const balanceFromBeforeInWei = await goldToken.balanceOf(from)

// Perform the transfer
await displaySendTx('gold.Transfer', goldToken.transfer(to, amountInWei.toString()))

// Check the balance after
const balanceFromAfterInWei = await goldToken.balanceOf(from)

// Get gas cost
const differenceInWei = balanceFromBeforeInWei.minus(balanceFromAfterInWei)
const gasCostInWei = differenceInWei.minus(amountInWei)
this.log(
`Transferred ${amountInWei} from ${from} to ${to}, gas cost: ${gasCostInWei.toString()} wei`
)
this.log(
`Balance of sender ${from} went down by ${differenceInWei.toString()} wei, final balance: ${balanceFromAfterInWei} Celo Gold wei`
)
await displaySendTx('gold.Transfer', goldToken.transfer(to, value.toFixed()))
}
}
51 changes: 51 additions & 0 deletions packages/cli/src/commands/election/activate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { sleep } from '@celo/utils/lib/async'
import { flags } from '@oclif/command'
import { BaseCommand } from '../../base'
import { newCheckBuilder } from '../../utils/checks'
import { displaySendTx } from '../../utils/cli'
import { Flags } from '../../utils/command'

export default class ElectionVote extends BaseCommand {
static description = 'Activate pending votes in validator elections to begin earning rewards'

static flags = {
...BaseCommand.flags,
from: Flags.address({ required: true, description: "Voter's address" }),
wait: flags.boolean({ description: 'Wait until all pending votes become activatable' }),
}

static examples = [
'activate --from 0x4443d0349e8b3075cba511a0a87796597602a0f1',
'activate --from 0x4443d0349e8b3075cba511a0a87796597602a0f1 --wait',
]
async run() {
const res = this.parse(ElectionVote)

this.kit.defaultAccount = res.flags.from
await newCheckBuilder(this, res.flags.from)
.isSignerOrAccount()
.runChecks()

const election = await this.kit.contracts.getElection()
const accounts = await this.kit.contracts.getAccounts()
const account = await accounts.voteSignerToAccount(res.flags.from)
const hasPendingVotes = await election.hasPendingVotes(account)
if (hasPendingVotes) {
if (res.flags.wait) {
// Spin until pending votes become activatable.
while (!(await election.hasActivatablePendingVotes(account))) {
await sleep(1000)
}
}
const txos = await election.activate(account)
for (const txo of txos) {
await displaySendTx('activate', txo, { from: res.flags.from })
}
if (txos.length === 0) {
this.log(`Pending votes not yet activatable. Consider using the --wait flag.`)
}
} else {
this.log(`No pending votes to activate`)
}
}
}
4 changes: 2 additions & 2 deletions packages/cli/src/commands/election/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ export default class List extends BaseCommand {
cli.action.stop()
cli.table(groupVotes, {
address: {},
votes: {},
capacity: {},
votes: { get: (g) => g.votes.toFixed() },
capacity: { get: (g) => g.capacity.toFixed() },
eligible: {},
})
}
Expand Down
41 changes: 41 additions & 0 deletions packages/cli/src/commands/election/revoke.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { flags } from '@oclif/command'
import BigNumber from 'bignumber.js'
import { BaseCommand } from '../../base'
import { newCheckBuilder } from '../../utils/checks'
import { displaySendTx } from '../../utils/cli'
import { Flags } from '../../utils/command'

export default class ElectionRevoke extends BaseCommand {
static description = 'Revoke votes for a Validator Group in validator elections.'

static flags = {
...BaseCommand.flags,
from: Flags.address({ required: true, description: "Voter's address" }),
for: Flags.address({
description: "ValidatorGroup's address",
required: true,
}),
value: flags.string({ description: 'Value of votes to revoke', required: true }),
}

static examples = [
'revoke --from 0x4443d0349e8b3075cba511a0a87796597602a0f1 --for 0x932fee04521f5fcb21949041bf161917da3f588b, --value 1000000',
]
async run() {
const res = this.parse(ElectionRevoke)

this.kit.defaultAccount = res.flags.from
await newCheckBuilder(this, res.flags.from)
.isSignerOrAccount()
.isValidatorGroup(res.flags.for)
.runChecks()

const election = await this.kit.contracts.getElection()
const accounts = await this.kit.contracts.getAccounts()
const account = await accounts.voteSignerToAccount(res.flags.from)
const txos = await election.revoke(account, res.flags.for, new BigNumber(res.flags.value))
for (const txo of txos) {
await displaySendTx('revoke', txo, { from: res.flags.from })
}
}
}
38 changes: 27 additions & 11 deletions packages/cli/src/commands/election/show.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,48 @@
import { flags } from '@oclif/command'
import { IArg } from '@oclif/parser/lib/args'
import { BaseCommand } from '../../base'
import { newCheckBuilder } from '../../utils/checks'
import { printValueMap } from '../../utils/cli'
import { printValueMapRecursive } from '../../utils/cli'
import { Args } from '../../utils/command'

export default class ElectionShow extends BaseCommand {
static description = 'Show election information about an existing Validator Group'
static description = 'Show election information about a voter or Validator Group'

static flags = {
...BaseCommand.flags,
voter: flags.boolean({
exclusive: ['group'],
description: 'Show information about an account voting in Validator elections',
}),
group: flags.boolean({
exclusive: ['voter'],
description: 'Show information about a group running in Validator elections',
}),
}

static args: IArg[] = [
Args.address('groupAddress', { description: "Validator Groups's address" }),
Args.address('address', { description: "Voter or Validator Groups's address" }),
]

static examples = ['show 0x97f7333c51897469E8D98E7af8653aAb468050a3']

async run() {
const { args } = this.parse(ElectionShow)
const address = args.groupAddress
const res = this.parse(ElectionShow)
const address = res.args.address
const election = await this.kit.contracts.getElection()

await newCheckBuilder(this)
.isValidatorGroup(address)
.runChecks()

const groupVotes = await election.getValidatorGroupVotes(address)
printValueMap(groupVotes)
if (res.flags.group) {
await newCheckBuilder(this)
.isValidatorGroup(address)
.runChecks()
const groupVotes = await election.getValidatorGroupVotes(address)
printValueMapRecursive(groupVotes)
} else if (res.flags.voter) {
await newCheckBuilder(this)
.isAccount(address)
.runChecks()
const voter = await election.getVoter(address)
printValueMapRecursive(voter)
}
}
}
8 changes: 7 additions & 1 deletion packages/cli/src/commands/election/vote.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { flags } from '@oclif/command'
import BigNumber from 'bignumber.js'
import { BaseCommand } from '../../base'
import { newCheckBuilder } from '../../utils/checks'
import { displaySendTx } from '../../utils/cli'
import { Flags } from '../../utils/command'

Expand All @@ -11,7 +12,7 @@ export default class ElectionVote extends BaseCommand {
...BaseCommand.flags,
from: Flags.address({ required: true, description: "Voter's address" }),
for: Flags.address({
description: "Set vote for ValidatorGroup's address",
description: "ValidatorGroup's address",
required: true,
}),
value: flags.string({ description: 'Amount of Gold used to vote for group', required: true }),
Expand All @@ -24,6 +25,11 @@ export default class ElectionVote extends BaseCommand {
const res = this.parse(ElectionVote)

this.kit.defaultAccount = res.flags.from
await newCheckBuilder(this, res.flags.from)
.isSignerOrAccount()
.isValidatorGroup(res.flags.for)
.runChecks()

const election = await this.kit.contracts.getElection()
const tx = await election.vote(res.flags.for, new BigNumber(res.flags.value))
await displaySendTx('vote', tx)
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/commands/exchange/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default class List extends BaseCommand {
const goldForDollar = await exchange.getBuyTokenAmount(parsedFlags.amount as string, false)
cli.action.stop()

this.log(`${parsedFlags.amount} cGLD => ${dollarForGold.toString()} cUSD`)
this.log(`${parsedFlags.amount} cUSD => ${goldForDollar.toString()} cGLD`)
this.log(`${parsedFlags.amount} cGLD => ${dollarForGold.toFixed()} cUSD`)
this.log(`${parsedFlags.amount} cUSD => ${goldForDollar.toFixed()} cGLD`)
}
}
4 changes: 2 additions & 2 deletions packages/cli/src/commands/exchange/selldollar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ export default class SellDollar extends BaseCommand {
const stableToken = await this.kit.contracts.getStableToken()
const exchange = await this.kit.contracts.getExchange()

await displaySendTx('approve', stableToken.approve(exchange.address, sellAmount.toString()))
await displaySendTx('approve', stableToken.approve(exchange.address, sellAmount.toFixed()))

const exchangeTx = exchange.exchange(sellAmount.toString(), minBuyAmount.toString(), false)
const exchangeTx = exchange.exchange(sellAmount.toFixed(), minBuyAmount.toFixed(), false)
await displaySendTx('exchange', exchangeTx)
}
}
4 changes: 2 additions & 2 deletions packages/cli/src/commands/exchange/sellgold.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ export default class SellGold extends BaseCommand {
const goldToken = await this.kit.contracts.getGoldToken()
const exchange = await this.kit.contracts.getExchange()

await displaySendTx('approve', goldToken.approve(exchange.address, sellAmount.toString()))
await displaySendTx('approve', goldToken.approve(exchange.address, sellAmount.toFixed()))

const exchangeTx = exchange.exchange(sellAmount.toString(), minBuyAmount.toString(), true)
const exchangeTx = exchange.exchange(sellAmount.toFixed(), minBuyAmount.toFixed(), true)
await displaySendTx('exchange', exchangeTx)
}
}
12 changes: 8 additions & 4 deletions packages/cli/src/commands/lockedgold/lock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default class Lock extends BaseCommand {
static args = []

static examples = [
'lock --from 0x47e172F6CfB6c7D01C1574fa3E2Be7CC73269D95 --value 1000000000000000000',
'lock --from 0x47e172F6CfB6c7D01C1574fa3E2Be7CC73269D95 --value 10000000000000000000000',
]

async run() {
Expand All @@ -28,14 +28,18 @@ export default class Lock extends BaseCommand {

this.kit.defaultAccount = address
const value = new BigNumber(res.flags.value)

await newCheckBuilder(this)
.addCheck(`Value [${value.toFixed()}] is not > 0`, () => value.gt(0))
.isAccount(address)
.runChecks()

const lockedGold = await this.kit.contracts.getLockedGold()
const pendingWithdrawalsValue = await lockedGold.getPendingWithdrawalsTotalValue(address)
const relockValue = BigNumber.minimum(pendingWithdrawalsValue, value)
const lockValue = value.minus(relockValue)

await newCheckBuilder(this)
.addCheck(`Value [${value.toString()}] is >= 0`, () => value.gt(0))
.isAccount(address)
.hasEnoughGold(address, lockValue)
.runChecks()

Expand All @@ -44,6 +48,6 @@ export default class Lock extends BaseCommand {
await displaySendTx('relock', txo, { from: address })
}
const tx = lockedGold.lock()
await displaySendTx('lock', tx, { value: lockValue.toString() })
await displaySendTx('lock', tx, { value: lockValue.toFixed() })
}
}
Loading

0 comments on commit a3fdf21

Please sign in to comment.