Skip to content

Instantly share code, notes, and snippets.

@flada-auxv
Created August 23, 2018 04:42
Show Gist options
  • Save flada-auxv/288ce359cb6eed399379b36ff62ae6ee to your computer and use it in GitHub Desktop.
Save flada-auxv/288ce359cb6eed399379b36ff62ae6ee to your computer and use it in GitHub Desktop.
[WIP] Calculate revenue from CryptoKitties' gen0 kitty sale (as in some block range)
// Calculate revenue from gen0 kitty sale (as in some block range)
//
// $ node /path/to/thisFile
// { wei: "9598226439521243188",
// ether: "9.598226439521243188",
// blockRange: "6090000...6100000",
// "salesCount(only gen0 kitty)": 117,
// "birthCount(only gen0 kitty)": 148,
// "SalesCount(all the kitties)": 458 }
const InfuraUrl = "https://mainnet.infura.io/v3/YOUR_INFURA_API_KEY"
const EtherscanAPIKey = "YOUR_ETHERSCAN_API_KY"
const axios = require("axios");
const BN = require("bn.js");
const Web3 = require("web3");
const web3 = new Web3();
const KittyCoreContract = {
address: "0x06012c8cf97BEaD5deAe237070F9587f8E7A266d",
events: {
birth: {
topic: "0x0a5311bd2a6608f08a180df2ee7c5946819a649b204b554bb8e39825b2c50ad5",
inputInterface: [
{
indexed: false,
name: "owner",
type: "address"
},{
indexed: false,
name: "kittyId",
type: "uint256"
},{
indexed: false,
name: "matronId",
type: "uint256"
},{
indexed: false,
name: "sireId",
type: "uint256"
},{
indexed: false,
name: "genes",
type: "uint256"
}
]
}
}
}
const SaleAuctionContract = {
address: "0xb1690C08E213a35Ed9bAb7B318DE14420FB57d8C",
events: {
auctionSaccessful: {
topic: "0x4fcc30d90a842164dd58501ab874a101a3749c3d4747139cefe7c876f4ccebd2",
inputInterface: [
{
indexed: false,
name: "tokenId",
type: "uint256"
},
{
indexed: false,
name: "totalPrice",
type: "uint256"
},
{
indexed: false,
name: "winner",
type: "address"
}
]
}
}
}
// Infura will time out in about two minutes, so need to limit block range up to 100K maybe, as of now
// Acctually, in order to total accumlated revenue up, need to send requests with series of divided block ranges several times and might need to persisted the response event logs locally
// or use your own node instead of Infura
const fromBlock = "0x5ced10" // 6_090_000
const toBlock = "0x5d1420" // 6_100_000
// const toBlock = "0x5e73b0" // 6_190_000
const options = {
fromBlock: fromBlock,
toBlock: toBlock,
address: KittyCoreContract.address,
topics: [KittyCoreContract.events.birth.topic]
}
getLogs(options)
.then(logs => {
return decodeLogs(logs, KittyCoreContract.events.birth.inputInterface)
}).then(decodedLogs => {
return kittyIdsCreatedByContractOwner(decodedLogs)
}).then(kittyIds => {
totalPrice(kittyIds)
});
function getLogs(options) {
const promise =
axios.post(InfuraUrl, {
jsonrpc: "2.0",
id: 1,
method: "eth_getLogs",
params: [options]
});
return promise.then((res) => {
return res.data.result
});
}
function decodeLogs(logs, inputInterface) {
const decodedLogs = logs.map(log => web3.eth.abi.decodeLog(inputInterface, log.data, log.topics))
return Promise.resolve(decodedLogs)
}
function kittyIdsCreatedByContractOwner(birthLogs) {
const filter = {
owner: ["0x0", KittyCoreContract.address],
matronId: "0",
sireId: "0"
};
const filtered =
birthLogs.filter(log => {
return filter.owner.includes(log.owner) &&
filter.matronId === log.matronId &&
filter.sireId === log.sireId
});
return Promise.resolve(filtered.map(log => log.kittyId))
}
function totalPrice(kittyIds) {
const options = {
fromBlock: fromBlock,
toBlock: toBlock,
address: SaleAuctionContract.address,
topics: [SaleAuctionContract.events.auctionSaccessful.topic]
}
return getLogs(options).then(logs => {
return decodeLogs(logs, SaleAuctionContract.events.auctionSaccessful.inputInterface)
}).then(decodedLogs => {
let firstPricesByTokenId = {}
const filtered = decodedLogs.filter(log => kittyIds.includes(log.tokenId))
filtered.forEach(log => {
if (log.tokenId in firstPricesByTokenId) {
// no-op because we need only first auction price to calculate AxiomZen"s revenue
} else {
firstPricesByTokenId[log.tokenId] = log.totalPrice
}
});
const prices = Object.values(firstPricesByTokenId)
const total = prices.map(e => new BN(e)).reduce((accm, current) => accm.add(current))
console.log({
wei: total.toString(),
ether: web3.utils.fromWei(total, "ether"),
blockRange: parseInt(fromBlock, 16) + "..." + parseInt(toBlock, 16),
"salesCount(only gen0 kitty)": Object.keys(firstPricesByTokenId).length,
"birthCount(only gen0 kitty)": kittyIds.length,
"SalesCount(all the kitties)": decodedLogs.length
});
});
}
function getABI(contractAddress) {
const promise =
axios.get("https://api.etherscan.io/api", {
params: {
module: "contract",
action: "getabi",
address: SaleAuctionContract.address,
apikey: EtherscanAPIKey
}
});
return promise.then(res => {
return JSON.parse(res.data.result)
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment