Created
August 23, 2018 04:42
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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