Skip to content

Commit

Permalink
Use ContractKit to get addresses for Blockchain API (#1175)
Browse files Browse the repository at this point in the history
  • Loading branch information
annakaz authored and ashishb committed Oct 7, 2019
1 parent 78afc93 commit 9a50adf
Show file tree
Hide file tree
Showing 13 changed files with 145 additions and 69 deletions.
8 changes: 3 additions & 5 deletions packages/blockchain-api/.env
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
EXCHANGE_RATES_API=https://api.exchangeratesapi.io
BLOCKSCOUT_API=https://alfajoresstaging-blockscout.celo-testnet.org/api
CELO_GOLD_ADDRESS=0x1313e2f3EBef8f0d869EECEb796D55A066eEA863
CELO_DOLLAR_ADDRESS=0x2df4dd6bd1b26a8503f763506bdb8e7cf165f69e
FAUCET_ADDRESS=0xF4314cb9046bECe6AA54bb9533155434d0c76909
BLOCKSCOUT_API=https://integration-blockscout.celo-testnet.org/api
FAUCET_ADDRESS=0x47e172F6CfB6c7D01C1574fa3E2Be7CC73269D95
VERIFICATION_REWARDS_ADDRESS=0xb4fdaf5f3cd313654aa357299ada901b1d2dd3b5
ATTESTATIONS_ADDRESS=0x8b7649116f169d2d2aebb6ea1a77f0baf31f2811
WEB3_PROVIDER_URL=https://integration-infura.celo-testnet.org/
5 changes: 1 addition & 4 deletions packages/blockchain-api/app.alfajores.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ env_variables:
DEPLOY_ENV: "alfajores"
EXCHANGE_RATES_API: "https://api.exchangeratesapi.io"
BLOCKSCOUT_API: "https://alfajores-blockscout.celo-testnet.org/api"
# Pull addresses from the build artifacts of the network in protocol/build
CELO_GOLD_ADDRESS: "0x11CD75C45638Ec9f41C0e8Df78fc756201E48ff2"
CELO_DOLLAR_ADDRESS: "0xd4b4fcaCAc9e23225680e89308E0a4C41Dd9C6B4"
FAUCET_ADDRESS: "0xCEa3eF8e187490A9d85A1849D98412E5D27D1Bb3"
VERIFICATION_REWARDS_ADDRESS: "0xb4fdaf5f3cd313654aa357299ada901b1d2dd3b5"
ATTESTATIONS_ADDRESS: "0x714f2879A4aa985508537f851FeBCfB26D7aF40D"
WEB3_PROVIDER_URL: "https://alfajores-infura.celo-testnet.org/"
5 changes: 1 addition & 4 deletions packages/blockchain-api/app.alfajoresstaging.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ env_variables:
DEPLOY_ENV: "alfajoresstaging"
EXCHANGE_RATES_API: "https://api.exchangeratesapi.io"
BLOCKSCOUT_API: "https://alfajoresstaging-blockscout.celo-testnet.org/api"
# Pull addresses from the build artifacts of the network in protocol/build
CELO_GOLD_ADDRESS: "0x1313e2f3EBef8f0d869EECEb796D55A066eEA863"
CELO_DOLLAR_ADDRESS: "0x2dF4dD6Bd1b26a8503F763506bdB8e7cf165f69E"
FAUCET_ADDRESS: "0xF4314cb9046bECe6AA54bb9533155434d0c76909"
VERIFICATION_REWARDS_ADDRESS: "0xb4fdaf5f3cd313654aa357299ada901b1d2dd3b5"
ATTESTATIONS_ADDRESS: "0x8B7649116f169D2D2aeBB6Ea1A77F0baF31F2811"
WEB3_PROVIDER_URL: "https://alfajoresstaging-infura.celo-testnet.org/"
12 changes: 0 additions & 12 deletions packages/blockchain-api/app.dev.yaml

This file was deleted.

5 changes: 1 addition & 4 deletions packages/blockchain-api/app.integration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ env_variables:
DEPLOY_ENV: "integration"
EXCHANGE_RATES_API: "https://api.exchangeratesapi.io"
BLOCKSCOUT_API: "https://integration-blockscout.celo-testnet.org/api"
# Pull addresses from the build artifacts of the network in protocol/build
CELO_GOLD_ADDRESS: "0x9102eCD93ac8D66bAc3D397BF52bc57Ee34Bcb87"
CELO_DOLLAR_ADDRESS: "0x47736AB66b892b0FCCb5c7d69B879C6141F6E80c"
FAUCET_ADDRESS: "0x47e172F6CfB6c7D01C1574fa3E2Be7CC73269D95"
VERIFICATION_REWARDS_ADDRESS: "0xb4fdaf5f3cd313654aa357299ada901b1d2dd3b5"
ATTESTATIONS_ADDRESS: "0xD2C0894D551D4C810Dc5CB55081c0b7BE965A118"
WEB3_PROVIDER_URL: "https://integration-infura.celo-testnet.org/"
5 changes: 1 addition & 4 deletions packages/blockchain-api/app.pilot.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ env_variables:
DEPLOY_ENV: "pilot"
EXCHANGE_RATES_API: "https://api.exchangeratesapi.io"
BLOCKSCOUT_API: "https://pilot-blockscout.celo-testnet.org/api"
# Pull addresses from the build artifacts of the network in protocol/build
CELO_GOLD_ADDRESS: "0xa69c3D18a74B3FD5F8aDA748428d0bfF8c5387Fe"
CELO_DOLLAR_ADDRESS: "0x996e24D7791A182f237635018c49E30cdA8FBa5e"
FAUCET_ADDRESS: "0x387bCb16Bfcd37AccEcF5c9eB2938E30d3aB8BF2"
VERIFICATION_REWARDS_ADDRESS: "0xb4fdaf5f3cd313654aa357299ada901b1d2dd3b5"
ATTESTATIONS_ADDRESS: "0x2cDEc3af5727dF2d490cF6068980E67dc6c19438"
WEB3_PROVIDER_URL: "https://pilot-infura.celo-testnet.org/"
5 changes: 1 addition & 4 deletions packages/blockchain-api/app.pilotstaging.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ env_variables:
DEPLOY_ENV: "pilotstaging"
EXCHANGE_RATES_API: "https://api.exchangeratesapi.io"
BLOCKSCOUT_API: "https://pilotstaging-blockscout.celo-testnet.org/api"
# Pull addresses from the build artifacts of the network in protocol/build
CELO_GOLD_ADDRESS: "0x2c9829C36c9D0802f04C6a06c423e132a3Ae1767"
CELO_DOLLAR_ADDRESS: "0x21C4A2C97eaE0c0E8c630FB3529F7718Fa37eCC1"
FAUCET_ADDRESS: "0x545DEBe3030B570731EDab192640804AC8Cf65CA"
VERIFICATION_REWARDS_ADDRESS: "0xb4fdaf5f3cd313654aa357299ada901b1d2dd3b5"
ATTESTATIONS_ADDRESS: "0xE3085194DFf8230EBccd5D6C55C59D52Fd9D5b90"
WEB3_PROVIDER_URL: "https://pilotstaging-infura.celo-testnet.org/"
1 change: 1 addition & 0 deletions packages/blockchain-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"deploy": "./deploy.sh"
},
"dependencies": {
"@celo/contractkit": "0.1.6",
"apollo-datasource-rest": "^0.3.1",
"apollo-server-express": "^2.4.2",
"bignumber.js": "^7.2.0",
Expand Down
69 changes: 53 additions & 16 deletions packages/blockchain-api/src/blockscout.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
import { RESTDataSource } from 'apollo-datasource-rest'
import BigNumber from 'bignumber.js'
import {
ATTESTATIONS_ADDRESS,
BLOCKSCOUT_API,
CONTRACT_SYMBOL_MAPPING,
FAUCET_ADDRESS,
VERIFICATION_REWARDS_ADDRESS,
} from './config'
import { BLOCKSCOUT_API, FAUCET_ADDRESS, VERIFICATION_REWARDS_ADDRESS } from './config'
import { EventArgs, EventInterface, EventTypes, TransferEvent } from './schema'
import { formatCommentString } from './utils'
import { formatCommentString, getContractAddresses } from './utils'

// to get rid of 18 extra 0s in the values
const WEI_PER_GOLD = Math.pow(10, 18)
Expand Down Expand Up @@ -51,6 +45,8 @@ export interface BlockscoutTransaction {
}

export class BlockscoutAPI extends RESTDataSource {
tokenAddressMapping: { [key: string]: string } | undefined
attestationsAddress: string | undefined
constructor() {
super()
this.baseURL = BLOCKSCOUT_API
Expand All @@ -67,6 +63,43 @@ export class BlockscoutAPI extends RESTDataSource {
return result
}

async ensureTokenAddresses() {
if (this.tokenAddressMapping && this.attestationsAddress) {
// Already got addresses
return
} else {
const addresses = await getContractAddresses()
this.attestationsAddress = addresses.attestationsAddress
this.tokenAddressMapping = addresses.tokenAddressMapping
}
}

getTokenAtAddress(tokenAddress: string) {
if (this.tokenAddressMapping) {
const lowerCaseTokenAddress = tokenAddress.toLowerCase()
if (lowerCaseTokenAddress in this.tokenAddressMapping) {
return this.tokenAddressMapping[lowerCaseTokenAddress]
} else {
console.info('Token addresses mapping: ' + JSON.stringify(this.tokenAddressMapping))
throw new Error(
'No token corresponding to ' +
lowerCaseTokenAddress +
'. Check web3 provider is for correct network.'
)
}
} else {
throw new Error('Cannot find tokenAddressMapping')
}
}

getAttestationAddress() {
if (this.attestationsAddress) {
return this.attestationsAddress
} else {
throw new Error('Cannot find attestation address')
}
}

// LIMITATION:
// This function will only return Gold transfers that happened via the GoldToken
// contract. Any native transfers of Gold will be omitted because of how blockscout
Expand All @@ -89,6 +122,7 @@ export class BlockscoutAPI extends RESTDataSource {
txHashToEventTransactions.set(tx.hash, currentTX)
}

await this.ensureTokenAddresses()
// Generate final events
txHashToEventTransactions.forEach((transactions: BlockscoutTransaction[], txhash: string) => {
// Exchange events have two corresponding transactions (in and out)
Expand All @@ -106,9 +140,9 @@ export class BlockscoutAPI extends RESTDataSource {
type: EventTypes.EXCHANGE,
timestamp: new BigNumber(inEvent.timeStamp).toNumber(),
block: new BigNumber(inEvent.blockNumber).toNumber(),
inSymbol: CONTRACT_SYMBOL_MAPPING[inEvent.contractAddress.toLowerCase()],
inSymbol: this.getTokenAtAddress(inEvent.contractAddress),
inValue: new BigNumber(inEvent.value).dividedBy(WEI_PER_GOLD).toNumber(),
outSymbol: CONTRACT_SYMBOL_MAPPING[outEvent.contractAddress.toLowerCase()],
outSymbol: this.getTokenAtAddress(outEvent.contractAddress),
outValue: new BigNumber(outEvent.value).dividedBy(WEI_PER_GOLD).toNumber(),
hash: txhash,
})
Expand All @@ -122,7 +156,8 @@ export class BlockscoutAPI extends RESTDataSource {
const [type, address] = resolveTransferEventType(
userAddress,
eventToAddress,
eventFromAddress
eventFromAddress,
this.getAttestationAddress()
)
events.push({
type,
Expand All @@ -131,7 +166,7 @@ export class BlockscoutAPI extends RESTDataSource {
value: new BigNumber(event.value).dividedBy(WEI_PER_GOLD).toNumber(),
address,
comment,
symbol: CONTRACT_SYMBOL_MAPPING[event.contractAddress.toLowerCase()] || 'unknown',
symbol: this.getTokenAtAddress(event.contractAddress) || 'unknown',
hash: txhash,
})
}
Expand All @@ -148,6 +183,7 @@ export class BlockscoutAPI extends RESTDataSource {
async getFeedRewards(args: EventArgs) {
const rewards: TransferEvent[] = []
const rawTransactions = await this.getTokenTransactions(args)
await this.ensureTokenAddresses()
for (const t of rawTransactions) {
// Only include verification rewards transfers
if (t.from.toLowerCase() !== VERIFICATION_REWARDS_ADDRESS) {
Expand All @@ -160,7 +196,7 @@ export class BlockscoutAPI extends RESTDataSource {
value: new BigNumber(t.value).dividedBy(WEI_PER_GOLD).toNumber(),
address: VERIFICATION_REWARDS_ADDRESS,
comment: t.input ? formatCommentString(t.input) : '',
symbol: CONTRACT_SYMBOL_MAPPING[t.contractAddress],
symbol: this.getTokenAtAddress(t.contractAddress),
hash: t.hash,
})
}
Expand All @@ -176,13 +212,14 @@ export class BlockscoutAPI extends RESTDataSource {
function resolveTransferEventType(
userAddress: string,
eventToAddress: string,
eventFromAddress: string
eventFromAddress: string,
attestationsAddress: string
): [EventTypes, string] {
if (eventToAddress === userAddress && eventFromAddress === FAUCET_ADDRESS) {
return [EventTypes.FAUCET, FAUCET_ADDRESS]
}
if (eventToAddress === ATTESTATIONS_ADDRESS && eventFromAddress === userAddress) {
return [EventTypes.VERIFICATION_FEE, ATTESTATIONS_ADDRESS]
if (eventToAddress === attestationsAddress && eventFromAddress === userAddress) {
return [EventTypes.VERIFICATION_FEE, attestationsAddress]
}
if (eventToAddress === userAddress && eventFromAddress === VERIFICATION_REWARDS_ADDRESS) {
return [EventTypes.VERIFICATION_REWARD, VERIFICATION_REWARDS_ADDRESS]
Expand Down
9 changes: 1 addition & 8 deletions packages/blockchain-api/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,9 @@ import dotenv from 'dotenv'
// Load environment variables from .env file
dotenv.config()

export const CONTRACT_SYMBOL_MAPPING: { [key: string]: string } = {
// GOLD_CONTRACT_ADDRESS
[(process.env.CELO_GOLD_ADDRESS as string).toLowerCase()]: 'Celo Gold',
// DOLLAR_CONTRACT_ADDRESS
[(process.env.CELO_DOLLAR_ADDRESS as string).toLowerCase()]: 'Celo Dollar',
}

export const EXCHANGE_RATES_API = (process.env.EXCHANGE_RATES_API as string).toLowerCase()
export const BLOCKSCOUT_API = (process.env.BLOCKSCOUT_API as string).toLowerCase()
export const FAUCET_ADDRESS = (process.env.FAUCET_ADDRESS as string).toLowerCase()
export const VERIFICATION_REWARDS_ADDRESS = (process.env
.VERIFICATION_REWARDS_ADDRESS as string).toLowerCase()
export const ATTESTATIONS_ADDRESS = (process.env.ATTESTATIONS_ADDRESS as string).toLowerCase()
export const WEB3_PROVIDER_URL = process.env.WEB3_PROVIDER_URL
55 changes: 55 additions & 0 deletions packages/blockchain-api/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
/* tslint:disable:no-console */
import { CeloContract, ContractKit, newKitFromWeb3 } from '@celo/contractkit'
import * as utf8 from 'utf8'
import Web3 from 'web3'
import coder from 'web3-eth-abi'
import { WEB3_PROVIDER_URL } from './config'

export function randomTimestamp() {
const start = new Date(2018, 0, 1)
Expand Down Expand Up @@ -40,3 +43,55 @@ export function formatCommentString(functionCallHex: string): string {
export function formatDateString(date: Date) {
return date.toISOString().split('T')[0]
}

let goldTokenAddress: string
let stableTokenAddress: string
let attestationsAddress: string
let tokenAddressMapping: { [key: string]: string }
export async function getContractAddresses() {
if (goldTokenAddress && stableTokenAddress && attestationsAddress) {
console.info('Already got token addresses')
return { tokenAddressMapping, attestationsAddress }
}
try {
const kit = await getContractKit()
goldTokenAddress = (await kit.registry.addressFor(CeloContract.StableToken)).toLowerCase()
stableTokenAddress = (await kit.registry.addressFor(CeloContract.GoldToken)).toLowerCase()

This comment has been minimized.

Copy link
@rohit-dua

rohit-dua Oct 9, 2019

Contributor

This should be opposite?

attestationsAddress = (await kit.registry.addressFor(CeloContract.Attestations)).toLowerCase()
tokenAddressMapping = {
[goldTokenAddress]: 'Celo Gold',
[stableTokenAddress]: 'Celo Dollar',
}
console.info(
'Got token addresses. Attestations: ' +
attestationsAddress +
' Token mapping: ' +
JSON.stringify(tokenAddressMapping)
)
return { tokenAddressMapping, attestationsAddress }
} catch (e) {
console.error('@getContractAddresses() error', e)
throw new Error('Unable to fetch contract addresses')
}
}

let contractKit: ContractKit
export async function getContractKit(): Promise<ContractKit> {
if (contractKit && (await contractKit.isListening())) {
// Already connected
return contractKit
}
try {
if (WEB3_PROVIDER_URL) {
const httpProvider = new Web3.providers.HttpProvider(WEB3_PROVIDER_URL)
const web3 = new Web3(httpProvider)
contractKit = newKitFromWeb3(web3)
return contractKit
} else {
throw new Error('Missing web3 provider URL, will not be able to fetch contract addresses.')
}
} catch (e) {
console.error('@getContractKit() error', e)
throw new Error('Failed to create contractKit instance')
}
}
16 changes: 16 additions & 0 deletions packages/blockchain-api/test/blockscout.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,22 @@ jest.mock('../src/config.ts', () => {
}
})

jest.mock('../src/utils.ts', () => {
const contractGetter = jest.fn()
const tokenAddressMapping: { [key: string]: string } = {
['0x000000000000000000000000000000000000gold']: 'Celo Gold',
['0x0000000000000000000000000000000000dollar']: 'Celo Dollar',
}
contractGetter.mockReturnValue({
tokenAddressMapping,
attestationsAddress: '0x0000000000000000000000000000000000a77357',
})
return {
...jest.requireActual('../src/utils.ts'),
getContractAddresses: contractGetter,
}
})

describe('Blockscout', () => {
let blockscoutAPI: BlockscoutAPI

Expand Down
Loading

0 comments on commit 9a50adf

Please sign in to comment.