Skip to content

Instantly share code, notes, and snippets.

@donpdonp
Last active August 26, 2020 17:45
Show Gist options
  • Save donpdonp/dfa52cb9ebe0f362905e1bf8b2d7f21a to your computer and use it in GitHub Desktop.
Save donpdonp/dfa52cb9ebe0f362905e1bf8b2d7f21a to your computer and use it in GitHub Desktop.
gluon defi CDP checker
(function() {
setup()
return { name: "cdp" }
})
var key = "defi:cdps"
var cache = []
var soon_cache = []
var soon_time = 15 * 60 * 1000 // 15min
var alert_channel = "#zrobo"
var infura_id, infura_url
function setup() {
db.get('cdp:api_key', function(key){
if(key) {
infura_id = key
infura_url = "https://mainnet.infura.io/v3/"+infura_id
bot.say(bot.admin_channel, "cdp: infura key loaded")
} else {
bot.say(bot.admin_channel, "cdp: infura key missing")
}
})
load(function(){
var soon_cache = expires_in(cache, soon_time)
bot.say(bot.admin_channel, "cdp cache "+cache.length+" "+soon_cache.length+" soons")
})
}
function go(msg) {
if (msg.method == "clocktower") {
var now = new Date(Date.parse(msg.params.time))
if (now.getMinutes() % 10 == 0) {
var fresh = cdps()
var news = expires_in(fresh)
fresh_check(news)
save(news, function(){ })
var soon_news = expires_in(fresh, soon_time)
var new_soons = not_in(soon_news, soon_cache)
if (new_soons.length > 0) {
summarize(new_soons, alert_channel)
}
}
}
if(msg.method == "irc.privmsg") {
var cmd = /^\!cdps?(\s+(\w+)\s+(\w+))?$/
var match = cmd.exec(msg.params.message)
if(match) {
if(match[2]) {
var c = cdp(match[2].toUpperCase(), match[3])
bot.say(msg.params.channel, "CDP "+match[2]+" "+match[3])
var price = parseFloat(coincap(c.cdpType).priceUsd)
bot.say(msg.params.channel, minisum(c, price))
} else {
var news = expires_in(cdps())
fresh_check(news)
save(news, function(){
summarize(news, msg.params.channel)
})
}
}
var cmd = /^\!geth(\s+(.*))?$/
var match = cmd.exec(msg.params.message)
if(match) {
if (match[2]) {
var parts = match[2].split(' ')
if (parts[0] == "balance") {
var blnce = balance(parts[1])
bot.say(msg.params.channel, "balance "+parts[1].substr(0,6)+" "+JSON.stringify(blnce))
}
}
}
}
}
function fresh_check(news) {
var new_ones = not_in(news, cache)
if (new_ones.length > 0) {
summarize(new_ones, alert_channel)
}
var old_ones = not_in(cache, news)
if (old_ones.length > 0) {
summarize(old_ones, alert_channel)
}
}
function summarize(news, channel) {
var prices = {}
var reports = news.reduce(function (m, c) {
if(c.cdpType) {
if(!prices[c.cdpType]) {
prices[c.cdpType] = coincap(c.cdpType)
}
var p = parseFloat(prices[c.cdpType].priceUsd)
var csum = minisum(c, p)
if(csum) {
m.push(csum)
}
}
return m
}, [])
if(reports.length > 0) {
reports.forEach(function(r){
bot.say(channel, r)
})
} else {
bot.say(channel, "no open CDPs")
}
}
function minisum(c, price) {
var expire = expire_time(c)
var timeleft = time_left(expire)
var timepart
if (timeleft > 0) {
timepart = (timeleft/1000/60/60).toFixed(1)+" hrs left"
} else {
timepart = "OVER "+expire
}
var part
if (c.soldFor) { // phase2
var bid = c.soldFor/c.soldColl
var discount = bid/price
var discount_words
if(discount < 1) {
discount_words = ((1-discount)*100).toFixed(1)+"% discount"
} else {
discount_words = ((discount-1)*100).toFixed(1)+"% premium"
}
var last_bid = "Last bid "+(bid).toFixed(2)+"DAI/"+c.cdpType+
"("+discount_words+" from "+price.toFixed(2)+"USD/"+c.cdpType+")"
part = c.soldColl.toFixed(3)+c.cdpType+" for "+c.soldFor.toFixed(2)+"DAI. "+last_bid+" "+timepart
} else {
part = "PHASE 1"
}
return "liquidation #"+c.liqId+": "+part
}
function expire_time(c) {
var time = new Date(c.timestamp)
return new Date(time.getTime() + 6 * 60 * 60 * 1000)
}
function time_left(expire) {
var now = new Date()
return expire - now
}
function cdp(token, id) {
var url = "https://defiexplore.com/api/liquidation/id/"+token+"/"+id
var json = http.get(url)
var data = JSON.parse(json)
return data
}
function cdps() {
var url = "https://defiexplore.com/api/liquidations/active/1"
var json = http.get(url)
var data = JSON.parse(json)
return data
}
function expires_in(cs, timeleft) {
var goods = cs.reduce(function(m, c){
if (timeleft) {
if (time_left(expire_time(c)) <= timeleft) {
m.push(c)
}
} else {
if (time_left(expire_time(c)) >= 0) {
m.push(c)
}
}
return m
}, [])
return goods
}
/*
{
"_id": "5eae6c6ee1aaee18106ded4d",
"liqId": 1,
"urn": "0xf8C7486EC3FD52Df96839DE68166ccbf96a8dE02",
"blockNum": 9991684,
"collateral": 0.004,
"debt": "27.5895512707359885",
"biteHash": "0xc5c980c280cd6e0b3e612f1e0975c209e55e72c4e534c1aa3d8e4a564c21aff8",
"finished": false,
"inHistory": true,
"timestamp": "2020-05-03T07:01:48.000Z",
"tend": 0,
"dent": 0,
"cdpId": "8787",
"__v": 0
},
*/
function load(cb) {
db.get(key, function(value){
if (value) {
cache = JSON.parse(value)
} else {
cache = []
}
cb()
})
}
function save(cdps, cb) {
var json = JSON.stringify(cdps)
db.set(key, json, function(){
cache = cdps
if (cb) {
cb()
}
})
}
function not_in(news, olds) {
var old_ids = olds.map(function(o){return o.cdpId})
return news.reduce(function(m,e){
// ignore stuck liquidation #1
if (e.liqId != 1 &&
time_left(expire_time(e)) > -1*1000*60*10 && // 10 minutes past expire (checks every 10 min)
old_ids.indexOf(e.cdpId) < 0) {
m.push(e)
}
return m
}, [])
}
function coincap(ticker) {
if (ticker=="WBTC") { ticker="BTC" }
var url = "https://api.coincap.io/v2/assets?limit=1&search="+encodeURIComponent(ticker)
try {
var json = http.get(url)
var quote = JSON.parse(json)
if (quote.data) {
return quote.data[0]
}
} catch(e) {
}
}
function balance(addr) {
var params = [addr, "latest"]
var result = geth("eth_getBalance", params)
if (result.result) {
return parseInt(result.result, 16) / 1000000000000000000
} else {
bot.say(bot.admin_channel, "infura error "+JSON.stringify(result))
}
}
function geth(method, params) {
var payload = {"jsonrpc": "2.0",
"method": method,
"params": params,
"id":1}
var body = JSON.stringify(payload)
var json = http.post(infura_url, body)
return JSON.parse(json)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment