-
Notifications
You must be signed in to change notification settings - Fork 375
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
Use ContractKit to get addresses for Blockchain API #1175
Changes from all commits
5dcd6b1
ca27c7e
c826422
e75a69b
3af9106
9f06fa1
07afac4
35ed13e
6e0f872
2d88565
91af0d6
e736508
d71eea1
1602013
99e46a6
659f239
d695af7
f80722e
de338f9
06ecea5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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/ |
This file was deleted.
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) | ||
|
@@ -51,6 +45,8 @@ export interface BlockscoutTransaction { | |
} | ||
|
||
export class BlockscoutAPI extends RESTDataSource { | ||
tokenAddressMapping: { [key: string]: string } | undefined | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any reason why we cache these values here in this class and also in the utils file? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I cached these both places to avoid passing them back and forth. I realize this isn't the cleanest solution, but the alternatives of:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Maybe I'm missing something but why does the class need to store the addresses at all when they'll always be available in util's module-level cache after first retrieval? Can't we just call |
||
attestationsAddress: string | undefined | ||
constructor() { | ||
super() | ||
this.baseURL = BLOCKSCOUT_API | ||
|
@@ -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 | ||
|
@@ -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) | ||
|
@@ -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, | ||
}) | ||
|
@@ -122,7 +156,8 @@ export class BlockscoutAPI extends RESTDataSource { | |
const [type, address] = resolveTransferEventType( | ||
userAddress, | ||
eventToAddress, | ||
eventFromAddress | ||
eventFromAddress, | ||
this.getAttestationAddress() | ||
) | ||
events.push({ | ||
type, | ||
|
@@ -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', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This one you provide a fallback value, but not the for the other uses of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The fallback above isn't provided as it's for an exchange with our Exchange contract, which must be between gold and dollars. The fallback here is for any token transfer, which could be of a new Token whose address we don't recognize |
||
hash: txhash, | ||
}) | ||
} | ||
|
@@ -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) { | ||
|
@@ -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, | ||
}) | ||
} | ||
|
@@ -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] | ||
|
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) | ||
|
@@ -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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. some error handling around this function contents would be good. |
||
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() | ||
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())) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto here |
||
// 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') | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@annakaz this isn't available on npm so we should use 0.1.5
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
0.1.6 has now been published, so leaving this version as is
https://www.npmjs.com/package/@celo/contractkit