Skip to content

Commit

Permalink
USDC related changes (#151)
Browse files Browse the repository at this point in the history
* chore(connection): updates docstring for `defaultFeeCurrency`

ERC20 address should be whitelisted, but this is not checked or enforced.

* refactor(kit): updates `setFeeCurrency`

- becomes synchronous call
- accepts `string` instead of `CeloTokenContract`

* chore(setFeeCurrency): adds hex type and address validation

Does NOT validate it's a whitelisted fee currency, but asserts it's a valid hexadecimal address.

* nit(connect): adds feeCurrency type todo

* chore(kit): adds error handling in `setFeeCurrency`

I checked how errors are handled in other places.
For example: https://github.com/celo-org/developer-tooling/blob/319d00d54569b421dc994cb7848b7c96c1b524bf/packages/sdk/base/src/contacts.ts#L15

* nit(contrackit): adds TODO in README code example

* chore(kit): adds `throws` docstring in `setFeeCurrency`

For better developer experience

* nit(contrackit): adds another TODO in README code example

* nit(cli): adds TODO related to `setFeeCurrency`

* nit(connect): adds TODO related to hex address typing

* chore(kit): uses `isAddress` instead of `isHexString`

- web3js > `isAddress`
- @celo/base > `isHexString`

* chore(cli): automatic linting

* nit(CONTRIBUTING): fixes incorrect hyperlink

* chore(cli): adds TODOs re fee currencies

* chore(cli): adds pseudo-code to fetch fee currency whitelist on-chain

* chore(contractkit): adds TODO in README

* chore(cli): implements `getGasOptions` helper function

Untested, not sure this works as expected.

* chore(cli): adds TODO notes on `gasOptions`

* test(cli): adds test for `getGasOptions` helper function

* nit(README): fixes title

To be consistent with other READMEs, which use the package name as title

* test(helpers): fixes bug that caused test to fail

Passes as expected now.

* chore(cli): adds TODO

* chore(helpers): removes `return await`

In favour of `return ... as Promise<..>` (which is the same as `return await ...`)

Also renames the function for better readability

* chore(cli): adds TODOs and updates example

* chore(cli): adds incomplete/WIP changes

Committing my current work in progress, before handing over to Nico

* feat: change feeCurrency to be a StrongAddress is most places

* chore: regenerate docs

* chore: upgrade celo abis cr11

* fix: only throw if the flag has been passed and the value is undefined

* fix: celocli tests

* fix: add feecurency whitelisted currencies

* test: add wrong feeCurrency test

* test: add failing tests

* fix: gas estimation when submitting a tx

* fix: test

* fix: proper gas/value check

* refactor: test displays the faulty value

* chore: regenerate docs

* fix: rollback to celo-legacy tx

* chore: regenerate docs

* fix: revert some unwanted chaanges

* fix: remove CELO from the whitelisted currencies

* refactor: Address->StrongAddress

* fix: tests failing due to StrongAddress typings

* feat(BREAKING): change default behaviour of transfer commands

to defaults to eip1559 if no --gasCurrency flag was passed

* fix: StrongAddress

* fix: type

* chore: regenerate docs

* fix: getting gas balance

* fix: ElectionActivate tests

* chore: regenerate docs

* feat: add FeeCurrencyWhitelist wrapper and refactor kit and cli to use it

* await registerAccount() to pass all checks for "shows no pending votes" election:activate test

* chore: remove pasted comments

* chore: regenerate docs

* fix: gasCurrency fallback

* chore: remove comment

* feat: handle adapted token in the whitelist

* fix: prevent unecessary network call for every command

* fix: ts-expect-error directive

* chore: regenerate docs

* chore: add changeset

* fix: handle feeCurrency capitalization

* fix: handle compatibility issues with config and gasCurrency

* refactor: some PR review feedback addressed

* fix: handle CELO as a gasCurrency

* chore: add one changeset per package changed

* test: test adapted tokens too

* fix: handle CELO capitalization

---------

Co-authored-by: Nicolas Brugneaux <[email protected]>
Co-authored-by: Leszek Stachowski <[email protected]>
Co-authored-by: Leszek Stachowski <[email protected]>
  • Loading branch information
3 people authored Mar 20, 2024
1 parent fd8c6c9 commit d6d82e8
Show file tree
Hide file tree
Showing 159 changed files with 1,949 additions and 985 deletions.
9 changes: 9 additions & 0 deletions .changeset/chatty-planets-trade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@celo/contractkit': major
---

The following changes are related to adding support for more fee currencies in the @celo packages.

(BREAKING): `setFeeCurrency` changed to accept an address instead of previously accepting a StableToken
(CHANGED): all places referring to gasCurrencies have been changed from `string` to `StrongAddress` for safer types. This shouldn't impact you as you already should have been giving `0x${string}` in these places
(CHANGED): reinforced the types of most contract wrappers to use `StrongAddress` where appropriate
9 changes: 9 additions & 0 deletions .changeset/khaki-lies-wait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@celo/celocli': major
---

The following changes are related to adding support for more fee currencies in the @celo packages.

(BREAKING): `--gasCurrency` changed to accept only whitelisted addresses or the string `CELO` instead of previously accepting a StableToken or 'auto'
(ADDED): `celocli network:whitelist` prints the whitelisted feeCurrencies
(ADDED): the cli will automagically convert the previous gasCurrency such as cEUR, cUSD, cREAL, CELO into its address if necessary
7 changes: 7 additions & 0 deletions .changeset/proud-days-hunt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@celo/utils': patch
---

The following changes are related to adding support for more fee currencies in the @celo packages.

(CHANGED): all places referring to gasCurrencies have been changed from `string` to `StrongAddress` for safer types. This shouldn't impact you as you already should have been giving `0x${string}` in these places
7 changes: 7 additions & 0 deletions .changeset/rude-hornets-smell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@celo/connect': patch
---

The following changes are related to adding support for more fee currencies in the @celo packages.

(CHANGED): all places referring to gasCurrencies have been changed from `string` to `StrongAddress` for safer types. This shouldn't impact you as you already should have been giving `0x${string}` in these places
7 changes: 7 additions & 0 deletions .changeset/seven-carrots-cross.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@celo/base': patch
---

The following changes are related to adding support for more fee currencies in the @celo packages.

(CHANGED): all places referring to gasCurrencies have been changed from `string` to `StrongAddress` for safer types. This shouldn't impact you as you already should have been giving `0x${string}` in these places
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ $ cd developer-tooling
### Installing Node.js

We use [Node.js](https://nodejs.org/en/) to run the project locally.
You need to install the **Node.js version** specified in [.nvmrc](../.nvmrc). To do so, run:
You need to install the **Node.js version** specified in [.nvmrc](/.nvmrc). To do so, run:

```sh
$ nvm install
Expand Down
7 changes: 6 additions & 1 deletion packages/cli/README.md

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

68 changes: 34 additions & 34 deletions packages/cli/src/base.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { StrongAddress } from '@celo/base'
import { ReadOnlyWallet } from '@celo/connect'
import { ContractKit, newKitFromWeb3, StableToken, Token } from '@celo/contractkit'
import { stableTokenInfos } from '@celo/contractkit/lib/celo-tokens'
import { ContractKit, newKitFromWeb3 } from '@celo/contractkit'
import { AzureHSMWallet } from '@celo/wallet-hsm-azure'
import { AddressValidation, newLedgerWalletWithSetup } from '@celo/wallet-ledger'
import { LocalWallet } from '@celo/wallet-local'
Expand All @@ -9,14 +9,9 @@ import { Command, Flags } from '@oclif/core'
import chalk from 'chalk'
import net from 'net'
import Web3 from 'web3'
import { CustomFlags } from './utils/command'
import { getGasCurrency, getNodeUrl } from './utils/config'
import { enumEntriesDupWithLowercase, requireNodeIsSynced } from './utils/helpers'
export const gasOptions = {
auto: 'auto',
Auto: 'auto',
...enumEntriesDupWithLowercase(Object.entries(Token)),
...enumEntriesDupWithLowercase(Object.entries(StableToken)),
}
import { requireNodeIsSynced } from './utils/helpers'

export abstract class BaseCommand extends Command {
static flags = {
Expand Down Expand Up @@ -46,12 +41,11 @@ export abstract class BaseCommand extends Command {
}
},
}),
gasCurrency: Flags.option({
options: Object.keys(gasOptions),
gasCurrency: CustomFlags.gasCurrency({
description:
"Use a specific gas currency for transaction fees (defaults to 'auto' which uses whatever feeCurrency is available)",
'Use a specific gas currency for transaction fees (defaults to CELO if no gas currency is supplied)',
hidden: true,
})(),
}),
useLedger: Flags.boolean({
default: false,
hidden: true,
Expand Down Expand Up @@ -201,28 +195,34 @@ export abstract class BaseCommand extends Command {
kit.defaultAccount = res.flags.from
}

const gasCurrencyConfig = res.flags.gasCurrency
? (gasOptions as any)[res.flags.gasCurrency]
: getGasCurrency(this.config.configDir)
const gasCurrencyFlag = (res.flags.gasCurrency ??
(await getGasCurrency(this.config.configDir, kit))) as StrongAddress | 'CELO' | undefined

if (gasCurrencyFlag && gasCurrencyFlag !== 'CELO') {
const feeCurrencyWhitelist = await kit.contracts.getFeeCurrencyWhitelist()
const validFeeCurrencies = await feeCurrencyWhitelist.getWhitelist()

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

const setStableTokenGas = async (stable: StableToken) => {
await kit.setFeeCurrency(stableTokenInfos[stable].contract)
}
if (Object.keys(StableToken).includes(gasCurrencyConfig)) {
await setStableTokenGas(StableToken[gasCurrencyConfig as keyof typeof StableToken])
} else if (gasCurrencyConfig === gasOptions.auto && kit.defaultAccount) {
const balances = await kit.getTotalBalance(kit.defaultAccount)
if (balances.CELO!.isZero()) {
const stables = Object.entries(StableToken)
for (const stable of stables) {
const stableName = stable[0]
const stableToken = stable[1]
// has balance
if ((balances as any)[stableName] && !(balances as any)[stableName].isZero()) {
await setStableTokenGas(stableToken)
break
}
}
throw new Error(
`${gasCurrencyFlag} is not a valid fee currency. Available currencies:\n${pairs.join(
'\n'
)}`
)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/account/set-wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default class SetWallet extends BaseCommand {
}),
signer: CustomFlags.address({
required: false,
default: '',
default: undefined,
description: 'Address of the signer key to verify proof of possession.',
}),
}
Expand Down
28 changes: 13 additions & 15 deletions packages/cli/src/commands/config/set.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BaseCommand, gasOptions } from '../../base'
import { readConfig, writeConfig } from '../../utils/config'
import { BaseCommand } from '../../base'
import { CeloConfig, readConfig, writeConfig } from '../../utils/config'
export default class Set extends BaseCommand {
static description = 'Configure running node information for propogating transactions to network'

Expand All @@ -9,17 +9,12 @@ export default class Set extends BaseCommand {
...BaseCommand.flags.node,
hidden: false,
},
gasCurrency: {
...BaseCommand.flags.gasCurrency,
hidden: false,
},
}

static examples = [
'set --node ws://localhost:2500',
'set --node <geth-location>/geth.ipc',
'set --gasCurrency cUSD',
'set --gasCurrency CELO',
'set --gasCurrency 0x874069Fa1Eb16D44d622F2e0Ca25eeA172369bC1',
]

requireSynced = false
Expand All @@ -28,12 +23,15 @@ export default class Set extends BaseCommand {
const res = await this.parse(Set)
const curr = readConfig(this.config.configDir)
const node = res.flags.node ?? curr.node
const gasCurrency = res.flags.gasCurrency
? (gasOptions as any)[res.flags.gasCurrency as string]
: curr.gasCurrency
writeConfig(this.config.configDir, {
node,
gasCurrency,
})
const gasCurrency = res.flags.gasCurrency ?? curr.gasCurrency

await writeConfig(
this.config.configDir,
{
node,
gasCurrency,
} as CeloConfig,
await this.getKit()
)
}
}
16 changes: 8 additions & 8 deletions packages/cli/src/commands/election/activate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
voteForGroupFrom,
} from '../../test-utils/chain-setup'
import { stripAnsiCodes, testLocally } from '../../test-utils/cliUtils'
import Activate from './activate'
import ElectionActivate from './activate'

process.env.NO_SYNCCHECK = 'true'

Expand All @@ -21,17 +21,17 @@ testWithGanache('election:activate', (web3: Web3) => {
})

it('fails when no flags are provided', async () => {
await expect(testLocally(Activate, [])).rejects.toThrow('Missing required flag from')
await expect(testLocally(ElectionActivate, [])).rejects.toThrow('Missing required flag from')
})

it('shows no pending votes', async () => {
const kit = newKitFromWeb3(web3)
const [userAddress] = await web3.eth.getAccounts()
const writeMock = jest.spyOn(ux.write, 'stdout')

registerAccount(kit, userAddress)
await registerAccount(kit, userAddress)

await testLocally(Activate, ['--from', userAddress])
await testLocally(ElectionActivate, ['--from', userAddress])

expect(writeMock.mock.calls).toMatchInlineSnapshot(`
[
Expand All @@ -53,7 +53,7 @@ testWithGanache('election:activate', (web3: Web3) => {
await registerAccountWithLockedGold(kit, userAddress)

await voteForGroupFrom(kit, userAddress, groupAddress, new BigNumber(10))
await testLocally(Activate, ['--from', userAddress])
await testLocally(ElectionActivate, ['--from', userAddress])

expect(writeMock.mock.calls).toMatchInlineSnapshot(`
[
Expand Down Expand Up @@ -83,7 +83,7 @@ testWithGanache('election:activate', (web3: Web3) => {
new BigNumber(0)
)

await testLocally(Activate, ['--from', userAddress])
await testLocally(ElectionActivate, ['--from', userAddress])

expect(writeMock.mock.calls).toMatchInlineSnapshot(`[]`)
expect((await election.getVotesForGroupByAccount(userAddress, groupAddress)).active).toEqual(
Expand All @@ -109,7 +109,7 @@ testWithGanache('election:activate', (web3: Web3) => {
)

await Promise.all([
testLocally(Activate, ['--from', userAddress, '--wait']),
testLocally(ElectionActivate, ['--from', userAddress, '--wait']),
new Promise<void>((resolve) => {
// at least the amount the --wait flag waits in the check
setTimeout(async () => {
Expand Down Expand Up @@ -166,7 +166,7 @@ testWithGanache('election:activate', (web3: Web3) => {
(await election.getVotesForGroupByAccount(otherUserAddress, groupAddress)).active
).toEqual(new BigNumber(0))

await testLocally(Activate, ['--from', otherUserAddress, '--for', userAddress])
await testLocally(ElectionActivate, ['--from', otherUserAddress, '--for', userAddress])

expect(writeMock.mock.calls).toMatchInlineSnapshot(`[]`)
expect((await election.getVotesForGroupByAccount(userAddress, groupAddress)).active).toEqual(
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/commands/election/activate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { newCheckBuilder } from '../../utils/checks'
import { displaySendTx } from '../../utils/cli'
import { CustomFlags } from '../../utils/command'

export default class ElectionVote extends BaseCommand {
export default class ElectionActivate extends BaseCommand {
static description =
'Activate pending votes in validator elections to begin earning rewards. To earn rewards as a voter, it is required to activate your pending votes at some point after the end of the epoch in which they were made.'

Expand All @@ -31,7 +31,7 @@ export default class ElectionVote extends BaseCommand {

async run() {
const kit = await this.getKit()
const res = await this.parse(ElectionVote)
const res = await this.parse(ElectionActivate)

const forAccount = res.flags.for ?? res.flags.from
await newCheckBuilder(this, forAccount).isSignerOrAccount().runChecks()
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/commands/election/list.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ElectionWrapper, ValidatorGroupVote } from '@celo/contractkit/lib/wrapp
import { ux } from '@oclif/core'
import BigNumber from 'bignumber.js'
import { testLocally } from '../../test-utils/cliUtils'
import List from './list'
import ElectionList from './list'

process.env.NO_SYNCCHECK = 'true'

Expand Down Expand Up @@ -33,7 +33,7 @@ describe('election:list cmd', () => {

const writeMock = jest.spyOn(ux.write, 'stdout')

await testLocally(List, ['--csv'])
await testLocally(ElectionList, ['--csv'])

expect(getValidatorGroupsVotesMock).toHaveBeenCalled()
expect(writeMock.mock.calls).toMatchInlineSnapshot(`
Expand Down
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 @@ -2,7 +2,7 @@ import { ux } from '@oclif/core'

import { BaseCommand } from '../../base'

export default class List extends BaseCommand {
export default class ElectionList extends BaseCommand {
static description =
'Prints the list of validator groups, the number of votes they have received, the number of additional votes they are able to receive, and whether or not they are eligible to elect validators.'

Expand All @@ -15,7 +15,7 @@ export default class List extends BaseCommand {

async run() {
const kit = await this.getKit()
const res = await this.parse(List)
const res = await this.parse(ElectionList)
ux.action.start('Fetching validator group vote totals')
const election = await kit.contracts.getElection()
const groupVotes = await election.getValidatorGroupsVotes()
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/src/commands/governance/approve.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Address } from '@celo/connect'
import { StrongAddress } from '@celo/base'
import { newKitFromWeb3 } from '@celo/contractkit'
import { GovernanceWrapper } from '@celo/contractkit/lib/wrappers/Governance'
import { NetworkConfig, testWithGanache, timeTravel } from '@celo/dev-utils/lib/ganache-test'
Expand All @@ -15,11 +15,11 @@ testWithGanache('governance:approve cmd', (web3: Web3) => {
const kit = newKitFromWeb3(web3)
const proposalID = '1'

let accounts: Address[] = []
let accounts: StrongAddress[] = []
let governance: GovernanceWrapper

beforeEach(async () => {
accounts = await web3.eth.getAccounts()
accounts = (await web3.eth.getAccounts()) as StrongAddress[]
kit.defaultAccount = accounts[0]
governance = await kit.contracts.getGovernance()
await governance
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/src/commands/governance/propose.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Address } from '@celo/connect'
import { StrongAddress } from '@celo/base'
import { newKitFromWeb3 } from '@celo/contractkit'
import { GoldTokenWrapper } from '@celo/contractkit/lib/wrappers/GoldTokenWrapper'
import { GovernanceWrapper } from '@celo/contractkit/lib/wrappers/Governance'
Expand Down Expand Up @@ -144,10 +144,10 @@ testWithGanache('governance:propose cmd', (web3: Web3) => {
const minDeposit = web3.utils.toWei(expConfig.minDeposit.toString(), 'ether')
const kit = newKitFromWeb3(web3)

let accounts: Address[] = []
let accounts: StrongAddress[] = []

beforeEach(async () => {
accounts = await web3.eth.getAccounts()
accounts = (await web3.eth.getAccounts()) as StrongAddress[]
kit.defaultAccount = accounts[0]
governance = await kit.contracts.getGovernance()
goldToken = await kit.contracts.getGoldToken()
Expand Down
Loading

0 comments on commit d6d82e8

Please sign in to comment.