Skip to content

Commit

Permalink
create erc-20 contract class
Browse files Browse the repository at this point in the history
  • Loading branch information
mholtzman committed Feb 18, 2022
1 parent 1ed8fdc commit 12601ad
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 119 deletions.
88 changes: 0 additions & 88 deletions main/abi/erc20.ts

This file was deleted.

60 changes: 32 additions & 28 deletions main/accounts/Account/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { isValidAddress, addHexPrefix } from 'ethereumjs-util'
import { Version } from 'eth-sig-util'

import { AccessRequest, AccountRequest, Accounts, RequestMode, TransactionRequest } from '..'
import { decodeContractCall } from '../../abi'
import erc20 from '../../abi/erc20'
import { decodeContractCall } from '../../contracts'
import erc20 from '../../contracts/erc20'
import nebulaApi from '../../nebula'
import signers from '../../signers'
import windows from '../../windows'
Expand All @@ -16,6 +16,7 @@ import { getType as getSignerType, Type as SignerType } from '../../signers/Sign

import provider from '../../provider'
import { ApprovalType } from '../../../resources/constants'
import Erc20Contract from '../../contracts/erc20'

const nebula = nebulaApi('accounts')

Expand Down Expand Up @@ -238,34 +239,37 @@ class FrameAccount {
}

private async checkForErc20Approve (req: TransactionRequest, calldata: string) {
const decodedData = erc20.decodeCallData(req.data.to || '', calldata)

if (decodedData && erc20.isApproval(decodedData)) {
const spender = decodedData.args[0].value
const amount = decodedData.args[1].value
const { decimals, name, symbol } = await erc20.getTokenData(provider, decodedData.contractAddress)

this.addRequiredApproval(
req,
ApprovalType.TokenSpendApproval,
{
decimals,
name,
symbol,
amount,
contract: decodedData.contractAddress
},
data => {
req.data.data = erc20.encodeFunctionData('approve', [spender, data.amount])

if (req.decodedData) {
req.decodedData.args[1].value = data.amount
}
const contractAddress = req.data.to
if (!contractAddress) return

const contract = new Erc20Contract(contractAddress, provider)
const decodedData = contract.decodeCallData(calldata)

if (decodedData && Erc20Contract.isApproval(decodedData)) {
const spender = decodedData.args[0].toLowerCase()
const amount = decodedData.args[1].toNumber()
const { decimals, name, symbol } = await contract.getTokenData()

this.addRequiredApproval(
req,
ApprovalType.TokenSpendApproval,
{
decimals,
name,
symbol,
amount,
contract: contractAddress
},
data => {
req.data.data = contract.encodeCallData('approve', [spender, data.amount])

if (req.decodedData) {
req.decodedData.args[1].value = data.amount
}
)
}
)

req.decodedData = decodedData
this.update()
this.update()
}
}

Expand Down
2 changes: 1 addition & 1 deletion main/accounts/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Version } from 'eth-sig-util'
import type { DecodedCallData } from '../abi'
import type { DecodedCallData } from '../contracts'
import type { Chain } from '../chains'
import type { TransactionData } from '../transaction'

Expand Down
73 changes: 73 additions & 0 deletions main/contracts/erc20.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { TransactionDescription } from '@ethersproject/abi'
import { Contract } from '@ethersproject/contracts'
import { Web3Provider } from '@ethersproject/providers'
import erc20Abi from '../externalData/balances/erc-20-abi'
import type { Provider } from '../provider'

function createWeb3ProviderWrapper (provider: Provider) {
const wrappedSend = (request: { method: string, params?: any[] }, cb: (error: any, response: any) => void) => {
provider.sendAsync({
method: request.method,
params: request.params || [],
id: 1,
jsonrpc: '2.0',
_origin: 'frame.eth' }, cb)
}

return {
sendAsync: wrappedSend,
send: wrappedSend
}
}

export default class Erc20Contract {
private contract: Contract

constructor (address: Address, provider: Provider) {
const web3Provider = new Web3Provider(createWeb3ProviderWrapper(provider))
this.contract = new Contract(address, erc20Abi, web3Provider)
}

static isApproval (data: TransactionDescription) {
return (
data.name === 'approve' &&
data.functionFragment.inputs.length === 2 &&
(data.functionFragment.inputs[0].name || '').toLowerCase().endsWith('spender') && data.functionFragment.inputs[0].type === 'address' &&
(data.functionFragment.inputs[1].name || '').toLowerCase().endsWith('value') && data.functionFragment.inputs[1].type === 'uint256'
)
}

decodeCallData (calldata: string) {
try {
return this.contract.interface.parseTransaction({ data: calldata })
} catch (e) {
// call does not match ERC-20 interface
}
}

encodeCallData (fn: string, params: any[]) {
return this.contract.interface.encodeFunctionData(fn, params)
}

async getTokenData () {
try {
const calls = await Promise.all([
this.contract.decimals(),
this.contract.name(),
this.contract.symbol()
])

return {
decimals: calls[0],
name: calls[1],
symbol: calls[2]
}
} catch (e) {
return {
decimals: 0,
name: '',
symbol: ''
}
}
}
}
File renamed without changes.
2 changes: 1 addition & 1 deletion package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
"@aragon/wrapper": "5.5.1",
"@ethereumjs/common": "2.6.0",
"@ethereumjs/tx": "3.4.0",
"@ethersproject/abi": "5.5.0",
"@ethersproject/contracts": "5.5.0",
"@githubprimer/octicons-react": "8.5.0",
"@ledgerhq/hw-app-eth": "6.23.1",
"@ledgerhq/hw-transport-node-hid-noevents": "6.20.0",
Expand Down

0 comments on commit 12601ad

Please sign in to comment.