Skip to content

Commit

Permalink
[cli] transfer:erc20 and balance commands (#7753)
Browse files Browse the repository at this point in the history
Command to transfer and check balance of erc20 contracts
  • Loading branch information
gastonponti authored Apr 26, 2021
1 parent 6c54a88 commit 372ffee
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 10 deletions.
24 changes: 19 additions & 5 deletions packages/cli/src/commands/account/balance.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,36 @@
import { BaseCommand } from '../../base'
import { printValueMap } from '../../utils/cli'
import { Args } from '../../utils/command'
import { failWith, printValueMap } from '../../utils/cli'
import { Args, Flags } from '../../utils/command'

export default class Balance extends BaseCommand {
static description = 'View Celo Dollar and Gold balances for an address'
static description = 'View Celo Stables and CELO balances for an address'

static flags = {
...BaseCommand.flags,
erc20Address: Flags.address({
description: 'Address of generic ERC-20 token to also check balance for',
}),
}

static args = [Args.address('address')]

static examples = ['balance 0x5409ed021d9299bf6814279a6a1411a7e866a631']
static examples = [
'balance 0x5409ed021d9299bf6814279a6a1411a7e866a631',
'balance 0x5409ed021d9299bf6814279a6a1411a7e866a631 --erc20Address 0x765DE816845861e75A25fCA122bb6898B8B1282a',
]

async run() {
const { args } = this.parse(Balance)
const { args, flags } = this.parse(Balance)

console.log('All balances expressed in units of 10^-18.')
printValueMap(await this.kit.getTotalBalance(args.address))
if (flags.erc20Address) {
try {
const erc20 = await this.kit.contracts.getErc20(flags.erc20Address)
printValueMap({ erc20: await erc20.balanceOf(args.address) })
} catch {
failWith('Invalid erc20 address')
}
}
}
}
2 changes: 1 addition & 1 deletion packages/cli/src/commands/exchange/show.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { StableTokenInfo } from '@celo/contractkit/src/celo-tokens'
import { StableTokenInfo } from '@celo/contractkit/lib/celo-tokens'
import { flags } from '@oclif/command'
import { cli } from 'cli-ux'
import { BaseCommand } from '../../base'
Expand Down
57 changes: 57 additions & 0 deletions packages/cli/src/commands/transfer/erc20.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Ierc20 } from '@celo/contractkit/lib/generated/IERC20'
import { Erc20Wrapper } from '@celo/contractkit/lib/wrappers/Erc20Wrapper'
import { flags } from '@oclif/command'
import BigNumber from 'bignumber.js'
import { BaseCommand } from '../../base'
import { newCheckBuilder } from '../../utils/checks'
import { displaySendTx, failWith } from '../../utils/cli'
import { Flags } from '../../utils/command'

export default class TransferErc20 extends BaseCommand {
static description = 'Transfer ERC20 to a specified address'

static flags = {
...BaseCommand.flags,
erc20Address: Flags.address({
required: true,
description: "Custom erc20 to check it's balance too",
}),
from: Flags.address({
required: true,
description: 'Address of the sender',
}),
to: Flags.address({
required: true,
description: 'Address of the receiver',
}),
value: flags.string({
required: true,
description: 'Amount to transfer (in wei)',
}),
}

static examples = [
'erc20 --erc20Address 0x765DE816845861e75A25fCA122bb6898B8B1282a --from 0xa0Af2E71cECc248f4a7fD606F203467B500Dd53B --to 0x5409ed021d9299bf6814279a6a1411a7e866a631 --value 10000000000000000000',
]

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

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

this.kit.defaultAccount = from
let celoToken: Erc20Wrapper<Ierc20>
try {
celoToken = await this.kit.contracts.getErc20(res.flags.erc20Address)
// this call allow us to check if it is a valid erc20
await celoToken.balanceOf(res.flags.from)
} catch {
failWith('Invalid erc20 address')
}
await newCheckBuilder(this).hasEnoughErc20(from, value, res.flags.erc20Address).runChecks()

await displaySendTx('transfer', celoToken.transfer(to, value.toFixed()))
}
}
10 changes: 10 additions & 0 deletions packages/cli/src/utils/checks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,16 @@ class CheckBuilder {
)
}

hasEnoughErc20 = (account: Address, value: BigNumber, erc20: Address) => {
const valueInEth = this.kit.connection.web3.utils.fromWei(value.toFixed(), 'ether')
return this.addCheck(`Account has at least ${valueInEth} erc20 token`, () =>
this.kit.contracts
.getErc20(erc20)
.then((goldToken) => goldToken.balanceOf(account))
.then((balance) => balance.gte(value))
)
}

exceedsProposalMinDeposit = (deposit: BigNumber) =>
this.addCheck(
`Deposit is greater than or equal to governance proposal minDeposit`,
Expand Down
16 changes: 12 additions & 4 deletions packages/docs/command-line-interface/account.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 34 additions & 0 deletions packages/docs/command-line-interface/transfer.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/sdk/contractkit/src/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export enum CeloContract {
DowntimeSlasher = 'DowntimeSlasher',
Election = 'Election',
EpochRewards = 'EpochRewards',
ERC20 = 'ERC20',
Escrow = 'Escrow',
Exchange = 'Exchange',
ExchangeEUR = 'ExchangeEUR',
Expand Down
7 changes: 7 additions & 0 deletions packages/sdk/contractkit/src/contract-cache.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { CeloContract } from './base'
import { StableToken } from './celo-tokens'
import { Ierc20 } from './generated/IERC20'
import { ContractKit } from './kit'
import { AccountsWrapper } from './wrappers/Accounts'
import { AttestationsWrapper } from './wrappers/Attestations'
import { BlockchainParametersWrapper } from './wrappers/BlockchainParameters'
import { DoubleSigningSlasherWrapper } from './wrappers/DoubleSigningSlasher'
import { DowntimeSlasherWrapper } from './wrappers/DowntimeSlasher'
import { ElectionWrapper } from './wrappers/Election'
import { Erc20Wrapper } from './wrappers/Erc20Wrapper'
// import { EpochRewardsWrapper } from './wrappers/EpochRewards'
import { EscrowWrapper } from './wrappers/Escrow'
import { ExchangeWrapper } from './wrappers/Exchange'
Expand All @@ -31,6 +33,7 @@ const WrapperFactories = {
[CeloContract.DowntimeSlasher]: DowntimeSlasherWrapper,
[CeloContract.Election]: ElectionWrapper,
// [CeloContract.EpochRewards]?: EpochRewardsWrapper,
[CeloContract.ERC20]: Erc20Wrapper,
[CeloContract.Escrow]: EscrowWrapper,
[CeloContract.Exchange]: ExchangeWrapper,
[CeloContract.ExchangeEUR]: ExchangeWrapper,
Expand Down Expand Up @@ -63,6 +66,7 @@ interface WrapperCacheMap {
[CeloContract.DowntimeSlasher]?: DowntimeSlasherWrapper
[CeloContract.Election]?: ElectionWrapper
// [CeloContract.EpochRewards]?: EpochRewardsWrapper
[CeloContract.ERC20]?: Erc20Wrapper<Ierc20>
[CeloContract.Escrow]?: EscrowWrapper
[CeloContract.Exchange]?: ExchangeWrapper
[CeloContract.ExchangeEUR]?: ExchangeWrapper
Expand Down Expand Up @@ -115,6 +119,9 @@ export class WrapperCache {
// getEpochRewards() {
// return this.getContract(CeloContract.EpochRewards)
// }
getErc20(address: string) {
return this.getContract(CeloContract.ERC20, address)
}
getEscrow() {
return this.getContract(CeloContract.Escrow)
}
Expand Down
5 changes: 5 additions & 0 deletions packages/sdk/contractkit/src/web3-contract-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { newFreezer } from './generated/Freezer'
import { newGasPriceMinimum } from './generated/GasPriceMinimum'
import { newGoldToken } from './generated/GoldToken'
import { newGovernance } from './generated/Governance'
import { newIerc20 } from './generated/IERC20'
import { newLockedGold } from './generated/LockedGold'
import { newMetaTransactionWallet } from './generated/MetaTransactionWallet'
import { newMetaTransactionWalletDeployer } from './generated/MetaTransactionWalletDeployer'
Expand All @@ -40,6 +41,7 @@ export const ContractFactories = {
[CeloContract.DowntimeSlasher]: newDowntimeSlasher,
[CeloContract.Election]: newElection,
[CeloContract.EpochRewards]: newEpochRewards,
[CeloContract.ERC20]: newIerc20,
[CeloContract.Escrow]: newEscrow,
[CeloContract.Exchange]: newExchange,
[CeloContract.ExchangeEUR]: newExchangeEur,
Expand Down Expand Up @@ -98,6 +100,9 @@ export class Web3ContractCache {
getEpochRewards() {
return this.getContract(CeloContract.EpochRewards)
}
getErc20(address: string) {
return this.getContract(CeloContract.ERC20, address)
}
getEscrow() {
return this.getContract(CeloContract.Escrow)
}
Expand Down

0 comments on commit 372ffee

Please sign in to comment.