Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[contractkit] Document methods #1195

Merged
merged 1 commit into from
Oct 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 35 additions & 11 deletions packages/contractkit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,25 @@ const kit = newKit('https://alfajores-infura.celo-testnet.org:8545')
To access web3:

```ts
const web3 = kit.web3

web3.eth.getBalance(someAddress)
await kit.web3.eth.getBalance(someAddress)
```

### Setting Default Tx Options

`kit` allows you to set default transaction options:

```ts
import { CeloContract } from '@celo/contractkit'

// default from
kit.defaultAccount = myAddress
// paid gas in celo dollars
await kit.setGasCurrency(CeloContract.StableToken)
import { newKit, CeloContract } from '@celo/contractkit'

async function getKit(myAddress: string) {
const kit = newKit('https://alfajores-infura.celo-testnet.org:8545')

// default from
kit.defaultAccount = myAddress
// paid gas in celo dollars
await kit.setGasCurrency(CeloContract.StableToken)
return kit
}
```

### Interacting with cGold & cDollar
Expand All @@ -60,7 +63,7 @@ celo-blockchain has two initial coins: cGold and cDollar (stableToken).
Both implement the ERC20 standard, and to interact with them is as simple as:

```ts
const goldtoken = await kit.contract.getGoldToken()
const goldtoken = await kit.contracts.getGoldToken()

const balance = await goldtoken.balanceOf(someAddress)
```
Expand All @@ -80,7 +83,7 @@ const receipt = await tx.waitReceipt()
To interact with cDollar, is the same but with a different contract:

```ts
const stabletoken = await kit.contract.getStableToken()
const stabletoken = await kit.contracts.getStableToken()
```

### Interacting with Other Contracts
Expand All @@ -92,6 +95,9 @@ For the moment, we have contract wrappers for:
- Exchange (Uniswap kind exchange between Gold and Stable tokens)
- Validators
- LockedGold
- GoldToken
- StableToken
- Attestations

In the following weeks will add wrapper for all other contracts

Expand All @@ -107,6 +113,24 @@ const web3Exchange = await kit._web3Contracts.getExchange()

We expose native wrappers for all Celo core contracts.

The complete list of Celo Core contracts is:

- Attestations
- LockedGold
- Escrow
- Exchange
- GasCurrencyWhitelist
- GasPriceMinimum
- GoldToken
- Governance
- MultiSig
- Random
- Registry
- Reserve
- SortedOracles
- StableToken
- Validators

## A Note About Contract Addresses

Celo Core Contracts addresses, can be obtained by looking at the `Registry` contract.
Expand Down
10 changes: 10 additions & 0 deletions packages/contractkit/src/address-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ const debug = debugFactory('kit:registry')
// Registry contract is always predeployed to this address
const REGISTRY_CONTRACT_ADDRESS = '0x000000000000000000000000000000000000ce10'

/**
* Celo Core Contract's Address Registry
*/
export class AddressRegistry {
private readonly registry: Registry
private readonly cache: Map<CeloContract, Address> = new Map()
Expand All @@ -19,6 +22,9 @@ export class AddressRegistry {
this.registry = newRegistry(kit.web3, REGISTRY_CONTRACT_ADDRESS)
}

/**
* Get the address for a `CeloContract`
*/
async addressFor(contract: CeloContract): Promise<Address> {
if (!this.cache.has(contract)) {
debug('Fetching address from Registry for %s', contract)
Expand All @@ -35,6 +41,10 @@ export class AddressRegistry {
return cachedAddress
}

/**
* Get the address for all possible `CeloContract`
*/

async allAddresses(): Promise<Record<CeloContract, Address>> {
const res: Partial<Record<CeloContract, Address>> = {}
for (const contract of AllContracts) {
Expand Down
8 changes: 8 additions & 0 deletions packages/contractkit/src/contract-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ interface WrapperCacheMap {
[CeloContract.Validators]?: ValidatorsWrapper
}

/**
* Kit ContractWrappers factory & cache.
*
* Provides access to all contract wrappers for celo core contracts
*/
export class WrapperCache {
// private wrapperCache: Map<CeloContract, any> = new Map()
private wrapperCache: WrapperCacheMap = {}
Expand Down Expand Up @@ -99,6 +104,9 @@ export class WrapperCache {
return this.getContract(CeloContract.Validators)
}

/**
* Get Contract wrapper
*/
public async getContract<C extends ValidWrappers>(contract: C) {
if (this.wrapperCache[contract] == null) {
const instance = await this.kit._web3Contracts.getContract(contract)
Expand Down
4 changes: 4 additions & 0 deletions packages/contractkit/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ export * from './kit'
export { CeloTransactionObject } from './wrappers/BaseWrapper'
export { Roles } from './wrappers/LockedGold'

/**
* Creates a new web3 instance
* @param url node url
*/
export function newWeb3(url: string) {
return new Web3(url)
}
39 changes: 38 additions & 1 deletion packages/contractkit/src/kit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,18 @@ import { ValidatorConfig } from './wrappers/Validators'

const debug = debugFactory('kit:kit')

/**
* Creates a new instance of `ContractKit` give a nodeUrl
* @param url CeloBlockchain node url
*/
export function newKit(url: string) {
return newKitFromWeb3(new Web3(url))
}

/**
* Creates a new instance of `ContractKit` give a web3 instance
* @param web3 Web3 instance
*/
export function newKitFromWeb3(web3: Web3) {
return new ContractKit(web3)
}
Expand All @@ -46,8 +54,11 @@ export interface KitOptions {
}

export class ContractKit {
/** core contract's address registry */
readonly registry: AddressRegistry
/** factory for core contract's native web3 wrappers */
readonly _web3Contracts: Web3ContractCache
/** factory for core contract's kit wrappers */
readonly contracts: WrapperCache

private config: KitOptions
Expand Down Expand Up @@ -100,7 +111,11 @@ export class ContractKit {
}
}

async setGasCurrency(token: CeloToken) {
/**
* Set CeloToken to use to pay for gas fees
* @param token cUsd or cGold
*/
async setGasCurrency(token: CeloToken): Promise<void> {
this.config.gasCurrency =
token === CeloContract.GoldToken ? null : await this.registry.addressFor(token)
}
Expand All @@ -109,11 +124,17 @@ export class ContractKit {
addLocalAccount(this.web3, privateKey)
}

/**
* Set default account for generated transactions (eg. tx.from )
*/
set defaultAccount(address: Address) {
this.config.from = address
this.web3.eth.defaultAccount = address
}

/**
* Default account for generated transactions (eg. tx.from)
*/
get defaultAccount(): Address {
return this.web3.eth.defaultAccount
}
Expand All @@ -126,6 +147,14 @@ export class ContractKit {
return this.config.gasInflationFactor
}

/**
* Set the ERC20 address for the token to use to pay for gas fees.
* The ERC20 must be whitelisted for gas.
*
* Set to `null` to use cGold
*
* @param address ERC20 address
*/
set defaultGasCurrency(address: Address | null) {
this.config.gasCurrency = address
}
Expand All @@ -142,6 +171,14 @@ export class ContractKit {
return this.web3.eth.isSyncing()
}

/**
* Send a transaction to celo-blockchain.
*
* Similar to `web3.eth.sendTransaction()` but with following differences:
* - applies kit tx's defaults
* - estimatesGas before sending
* - returns a `TransactionResult` instead of `PromiEvent`
*/
async sendTransaction(tx: Tx): Promise<TransactionResult> {
tx = this.fillTxDefaults(tx)

Expand Down
12 changes: 12 additions & 0 deletions packages/contractkit/src/utils/tx-result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,20 @@ import { TransactionReceipt } from 'web3/types'

const debug = debugFactory('kit:tx:result')

/**
* Transforms a `PromiEvent` to a `TransactionResult`.
*
* PromiEvents are returned by web3 when we do a `contract.method.xxx.send()`
*/
export function toTxResult(pe: PromiEvent<any>) {
return new TransactionResult(pe)
}

/**
* Replacement interface for web3's `PromiEvent`. Instead of emiting events
* to signal different stages, eveything is exposed as a promise. Which ends
* up being nicer when doing promise/async based programming.
*/
export class TransactionResult {
private hashFuture = new Future<string>()
private receiptFuture = new Future<TransactionReceipt>()
Expand All @@ -34,10 +44,12 @@ export class TransactionResult {
}) as any)
}

/** Get (& wait for) transaction hash */
getHash() {
return this.hashFuture.wait()
}

/** Get (& wait for) transaction receipt */
waitReceipt() {
return this.receiptFuture.wait()
}
Expand Down
11 changes: 11 additions & 0 deletions packages/contractkit/src/web3-contract-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ const ContractFactories = {
type CFType = typeof ContractFactories
type ContractCacheMap = { [K in keyof CFType]?: ReturnType<CFType[K]> }

/**
* Native Web3 contracts factory and cache.
*
* Exposes accessors to all `CeloContract` web3 contracts.
*
* Mostly a private cache, kit users would normally use
* a contract wrapper
*/
export class Web3ContractCache {
private cacheMap: ContractCacheMap = {}

Expand Down Expand Up @@ -86,6 +94,9 @@ export class Web3ContractCache {
return this.getContract(CeloContract.Validators)
}

/**
* Get native web3 contract wrapper
*/
async getContract<C extends keyof typeof ContractFactories>(contract: C) {
if (this.cacheMap[contract] == null) {
debug('Initiating contract %s', contract)
Expand Down
13 changes: 13 additions & 0 deletions packages/contractkit/src/wrappers/BaseWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,37 @@ import { TransactionReceipt } from 'web3/types'
import { ContractKit } from '../kit'
import { TransactionResult } from '../utils/tx-result'

/** Represents web3 native contract Method */
type Method<I extends any[], O> = (...args: I) => TransactionObject<O>

export type NumberLike = string | number | BigNumber

/** Base ContractWrapper */
export abstract class BaseWrapper<T extends Contract> {
constructor(protected readonly kit: ContractKit, protected readonly contract: T) {}

/** Contract address */
get address(): string {
// TODO fix typings
return (this.contract as any)._address
}
}

export interface CeloTransactionObject<O> {
/** web3 native TransactionObject. Normally not used */
txo: TransactionObject<O>
/** send the transaction to the chain */
send(params?: Omit<Tx, 'data'>): Promise<TransactionResult>
/** send the transaction and waits for the receipt */
sendAndWaitForReceipt(params?: Omit<Tx, 'data'>): Promise<TransactionReceipt>
}

/** Parse string -> BigNumber */
export function toBigNumber(input: string) {
return new BigNumber(input)
}

/** Parse string -> int */
export function toNumber(input: string) {
return parseInt(input, 10)
}
Expand All @@ -39,10 +47,15 @@ export function parseNumber(input: NumberLike) {

type Parser<A, B> = (input: A) => B

/** Identity Parser */
export function identity<A>(a: A) {
return a
}

/**
* Tuple parser
* Useful to map different input arguments
*/
export function tupleParser<A0, B0>(parser0: Parser<A0, B0>): (...args: [A0]) => [B0]
export function tupleParser<A0, B0, A1, B1>(
parser0: Parser<A0, B0>,
Expand Down
Loading