Last active
August 26, 2020 17:45
-
-
Save donpdonp/dfa52cb9ebe0f362905e1bf8b2d7f21a to your computer and use it in GitHub Desktop.
gluon defi CDP checker
This file contains hidden or 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
(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