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

added tokens interface #7

Merged
merged 12 commits into from
Oct 13, 2021
Merged
Show file tree
Hide file tree
Changes from 7 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
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 --detectOpenHandles --verbose",
chescalante marked this conversation as resolved.
Show resolved Hide resolved
"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
52 changes: 52 additions & 0 deletions src/lib/token/BaseToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {
BigNumber,
BigNumberish,
providers,
Signer,
ContractTransaction,
} from 'ethers'

export type TokenType = 'erc20' | 'rbtc'

export class BaseToken {
get signer(): Signer | null {
return Signer.isSigner(this.providerOrSigner) ? this.providerOrSigner : null
}

get provider(): providers.Provider {
return Signer.isSigner(this.providerOrSigner)
? this.providerOrSigner.provider!
: this.providerOrSigner
}
ilanolkies marked this conversation as resolved.
Show resolved Hide resolved

constructor(
public providerOrSigner: providers.Provider | Signer,
public logo: string,
) {}
ilanolkies marked this conversation as resolved.
Show resolved Hide resolved

protected async account() {
chescalante marked this conversation as resolved.
Show resolved Hide resolved
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)
98 changes: 98 additions & 0 deletions src/lib/token/ERC20Token.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
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 erc677Factory = new ERC677__factory(account)
const erc20 = (await erc677Factory.deploy(
accountAddress,
BigNumber.from(10).mul(tenPow(TEST_TOKEN_DECIMALS)),
'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.eq(10)).toBe(true)
})

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)
})
})
62 changes: 62 additions & 0 deletions src/lib/token/ERC20Token.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import {
BigNumber,
BigNumberish,
ContractTransaction,
providers,
Signer,
} from 'ethers'
import {
BaseToken,
IToken,
ITransferOptions,
tenPow,
TokenType,
} from './BaseToken'
import { ERC20 as ERC20Type, ERC20__factory } from './types'

class ERC20Token extends BaseToken implements IToken {
private tokenContract: ERC20Type

constructor(
public address: string,
providerOrSigner: providers.Provider | Signer,
logo: string,
) {
super(providerOrSigner, logo)
this.tokenContract = ERC20__factory.connect(address, providerOrSigner)
}

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.account()

const decimals = await this.decimals()

const balance = await this.tokenContract.balanceOf(account)

return balance.div(tenPow(decimals))
}

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have some concerns:

  1. This is not consistent int units. One returns the real value and the other uses the blockchain value
  2. Is this working with decimals? I guess not...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure about that but I test it and it doesn't work, so I made a few changes to remove the div I believe we don't need that for now. Let me know what you think about it.

})
ilanolkies marked this conversation as resolved.
Show resolved Hide resolved
}
}

export { ERC20Token }
76 changes: 76 additions & 0 deletions src/lib/token/RBTCToken.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
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 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()

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

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

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

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)
})
})
57 changes: 57 additions & 0 deletions src/lib/token/RBTCToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import {
BigNumber,
BigNumberish,
ContractTransaction,
providers,
Signer,
} from 'ethers'
import {
BaseToken,
IToken,
ITransferOptions,
tenPow,
TokenType,
} from './BaseToken'

class RBTCToken extends BaseToken implements IToken {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I are only working with testnet now. Let's add TRBTC too. I guess it is a different entity, maybe inherits all the same but overriding the symbol

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made a few changes regarding this, let me know if it's ok for now.

constructor(providerOrSigner: providers.Provider | Signer, logo: string) {
super(providerOrSigner, logo)
}

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

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

public async symbol(): Promise<string> {
return 'RBTC'
}
ilanolkies marked this conversation as resolved.
Show resolved Hide resolved

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

const balance = await this.signer!.getBalance()

return balance.div(tenPow(decimals))
}

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

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

export { RBTCToken }
Loading