Skip to content

Commit

Permalink
added tokens interface (#7)
Browse files Browse the repository at this point in the history
* added tokens interface

* added tests

* fix lock

* added ganache to ci

* fix android build

* fix ci android

* review changes

* Update src/lib/token/BaseToken.ts

Co-authored-by: Ilan <[email protected]>

* review changes

* Update package.json

Co-authored-by: Ilan <[email protected]>

* refactor and added TRBTC

* removed balance div

Co-authored-by: Christian Escalante <[email protected]>
Co-authored-by: Ilan <[email protected]>
  • Loading branch information
3 people authored Oct 13, 2021
1 parent 6bb8794 commit 594e437
Show file tree
Hide file tree
Showing 20 changed files with 3,601 additions and 1,137 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: |
yarn
yarn test
yarn lint
run: yarn
run: yarn ganache &
run: yarn test
run: yarn lint

- name: Build Android Release
run: |
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ jobs:
with:
node-version: 12.x
- run: yarn
- run: yarn ganache &
- run: yarn test
- run: yarn lint
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"android": "react-native run-android",
"ios": "react-native run-ios",
"start": "react-native start",
"test": "jest",
"test": "jest --verbose",
"ganache": "npx ganache-cli",
"test:watch": "jest --watch",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
"postinstall": "rn-nodeify --install buffer,events,stream --hack"
Expand All @@ -16,6 +17,8 @@
"@react-navigation/stack": "^6.0.9",
"@rsksmart/rif-id-ethr-did": "^0.1.0",
"@rsksmart/rif-id-mnemonic": "^0.1.0",
"@rsksmart/rsk-contract-metadata": "^1.0.15",
"@rsksmart/rsk-testnet-contract-metadata": "^1.0.11",
"buffer": "^4.9.2",
"ethers": "^5.4.7",
"events": "^1.1.1",
Expand Down
39 changes: 39 additions & 0 deletions src/lib/token/BaseToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { BigNumber, BigNumberish, Signer, ContractTransaction } from 'ethers'

export type TokenType = 'erc20' | 'rbtc'

export class BaseToken {
public signer: Signer
public logo: string

constructor(signer: Signer, logo: string) {
this.signer = signer
this.logo = logo
}

protected async getAddress() {
return await this.signer.getAddress()
}
}

export interface ITransferOptions {
gasPrice: BigNumberish
gasLimit: BigNumberish
nonce: BigNumberish
}

export interface IToken {
getType: () => TokenType
decimals: () => Promise<number>
symbol: () => Promise<string>
balance: () => Promise<BigNumber>
transfer: (
recipientAddress: string,
amount: BigNumberish,
options?: ITransferOptions,
) => Promise<ContractTransaction>
logo: string
}

export const ten = BigNumber.from(10)
export const tenPow = (exp: BigNumberish) => ten.pow(exp)
99 changes: 99 additions & 0 deletions src/lib/token/ERC20Token.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { providers, BigNumber } from 'ethers'
import { tenPow } from './BaseToken'
import { ERC20Token } from './ERC20Token'
import { ERC20__factory } from './types'
import { ERC677__factory } from './types/factories/ERC677__factory'

const Config = {
BLOCKCHAIN_HTTP_URL: 'HTTP://127.0.0.1:8545',
}

const TEST_TOKEN_DECIMALS = 18

const getJsonRpcProvider = async (): Promise<providers.JsonRpcProvider> => {
return new providers.JsonRpcProvider(Config.BLOCKCHAIN_HTTP_URL)
}

const getSigner = async (index: number = 0) => {
const provider = await getJsonRpcProvider()
return provider.getSigner(index)
}

describe('ERC20 token', () => {
let erc20Token: ERC20Token | null = null
let tokenAddress = ''

beforeEach(async () => {
const account = await getSigner()
const accountAddress = await account.getAddress()

// using ERC677__factory that supports ERC20 to set totalSupply (just for testing purpose)
const initialSupply = BigNumber.from(10).mul(tenPow(TEST_TOKEN_DECIMALS))
const erc677Factory = new ERC677__factory(account)
const erc20 = (await erc677Factory.deploy(
accountAddress,
initialSupply,
'TEST_ERC20',
'TEST_ERC20',
)) as any

tokenAddress = erc20.address

erc20Token = new ERC20Token(tokenAddress, account, 'logo.jpg')
})

test('get symbol', async () => {
const symbol = await erc20Token!.symbol()

expect(symbol).toBe('TEST_ERC20')
})

test('get logo', async () => {
const logo = erc20Token!.logo

expect(logo).toBe('logo.jpg')
})

test('get decimals', async () => {
const decimals = await erc20Token!.decimals()

expect(decimals).toBe(TEST_TOKEN_DECIMALS)
})

test('get type', async () => {
const type = erc20Token!.getType()

expect(type).toBe('erc20')
})

test('get balance', async () => {
const result = await erc20Token!.balance()

expect(result.toString()).toBe('10000000000000000000')
})

test('transfer', async () => {
const from = await getSigner()
const fromAddress = await from.getAddress()
const to = await getSigner(1)
const toAddress = await to.getAddress()

const amountToTransfer = BigNumber.from(100)

const sender = ERC20__factory.connect(tokenAddress, from)

const balanceSender = await sender.balanceOf(fromAddress)

expect(balanceSender.gte(amountToTransfer)).toBe(true)

const transferTx = await erc20Token!.transfer(toAddress, amountToTransfer)

await transferTx.wait()

const recipient = ERC20__factory.connect(tokenAddress, to)

const balanceRecipient = await recipient.balanceOf(toAddress)

expect(balanceRecipient.eq(amountToTransfer)).toBe(true)
})
})
40 changes: 40 additions & 0 deletions src/lib/token/ERC20Token.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { BigNumber, BigNumberish, ContractTransaction, Signer } from 'ethers'
import { BaseToken, IToken, ITransferOptions, TokenType } from './BaseToken'
import { ERC20 as ERC20Type, ERC20__factory } from './types'

class ERC20Token extends BaseToken implements IToken {
private tokenContract: ERC20Type

constructor(address: string, signer: Signer, logo: string) {
super(signer, logo)
this.tokenContract = ERC20__factory.connect(address, signer)
}

public getType(): TokenType {
return 'erc20'
}

public async decimals(): Promise<number> {
return this.tokenContract.decimals()
}

public async symbol(): Promise<string> {
return this.tokenContract.symbol()
}

public async balance(): Promise<BigNumber> {
const account = await this.getAddress()

return this.tokenContract.balanceOf(account)
}

public async transfer(
recipientAddress: string,
amount: BigNumberish,
options?: ITransferOptions,
): Promise<ContractTransaction> {
return this.tokenContract.transfer(recipientAddress, amount, options ?? {})
}
}

export { ERC20Token }
77 changes: 77 additions & 0 deletions src/lib/token/RBTCToken.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { providers, BigNumber } from 'ethers'
import { RBTCToken } from './RBTCToken'

const Config = {
BLOCKCHAIN_HTTP_URL: 'HTTP://127.0.0.1:8545',
}

const TEST_TOKEN_DECIMALS = 18
const CHAIN_ID = 31

const getJsonRpcProvider = async (): Promise<providers.JsonRpcProvider> => {
return new providers.JsonRpcProvider(Config.BLOCKCHAIN_HTTP_URL)
}

const getSigner = async (index: number = 0) => {
const provider = await getJsonRpcProvider()
return provider.getSigner(index)
}

describe('RBTC token', () => {
let rbtcToken: RBTCToken | null = null

beforeEach(async () => {
const account = await getSigner(9)

rbtcToken = new RBTCToken(account, 'logo.jpg', CHAIN_ID)
})

test('get symbol', async () => {
const symbol = await rbtcToken!.symbol()

expect(symbol).toBe('TRBTC')
})

test('get logo', async () => {
const logo = rbtcToken!.logo

expect(logo).toBe('logo.jpg')
})

test('get decimals', async () => {
const decimals = await rbtcToken!.decimals()

expect(decimals).toBe(TEST_TOKEN_DECIMALS)
})

test('get type', async () => {
const type = rbtcToken!.getType()

expect(type).toBe('rbtc')
})

test('get balance', async () => {
const result = await rbtcToken!.balance()

expect(result.gt(0)).toBe(true)
})

test('transfer', async () => {
const to = await getSigner(1)
const toAddress = await to.getAddress()

const balanceRecipientInitial = await to.getBalance()

const amountToTransfer = BigNumber.from(100)

const transferTx = await rbtcToken!.transfer(toAddress, amountToTransfer)

await transferTx.wait()

const balanceRecipient = await to.getBalance()

expect(
balanceRecipient.eq(balanceRecipientInitial.add(amountToTransfer)),
).toBe(true)
})
})
46 changes: 46 additions & 0 deletions src/lib/token/RBTCToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { BigNumber, BigNumberish, ContractTransaction, Signer } from 'ethers'
import { BaseToken, IToken, ITransferOptions, TokenType } from './BaseToken'
import { MAINNET_CHAINID } from './tokenMetadata'

class RBTCToken extends BaseToken implements IToken {
public chainId: number

constructor(signer: Signer, logo: string, chainId: number) {
super(signer, logo)

this.chainId = chainId
}

public getType(): TokenType {
return 'rbtc'
}

public async decimals(): Promise<number> {
return 18
}

public async symbol(): Promise<string> {
return this.chainId === MAINNET_CHAINID ? 'RBTC' : 'TRBTC'
}

public async balance(): Promise<BigNumber> {
return this.signer.getBalance()
}

public async transfer(
recipientAddress: string,
amount: BigNumberish,
options?: ITransferOptions,
): Promise<ContractTransaction> {
const account = await this.getAddress()

return this.signer.sendTransaction({
from: account,
to: recipientAddress,
value: amount,
...options,
})
}
}

export { RBTCToken }
21 changes: 21 additions & 0 deletions src/lib/token/assets/RBTC-mainnet.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 594e437

Please sign in to comment.