diff --git a/package-lock.json b/package-lock.json index fa16f36d45..d311bd829c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "blakejs": "^1.2.1", "bn.js": "^5.2.1", "borsh": "^0.7.0", + "curve25519-js": "^0.0.4", "dotenv": "^8.6.0", "ethers": "^5.6.5", "graphql": "^16.6.0", @@ -27,6 +28,7 @@ "hi-base32": "^0.5.1", "js-sha512": "^0.8.0", "limiter": "2.1.0", + "miscreant": "^0.3.2", "p-limit": "^3.1.0", "starknet": "^4.17.1", "tron-format-address": "^0.1.8", @@ -785,9 +787,9 @@ } }, "node_modules/@defillama/sdk": { - "version": "4.0.64", - "resolved": "https://registry.npmjs.org/@defillama/sdk/-/sdk-4.0.64.tgz", - "integrity": "sha512-wqM4enhx11vN5OuRNpK9QMoOEQbbQFKu7tT0rM6ddYA4w1tvuayWAn8mjfV8TN5wd88aHV4+zNY0Sps+mrVN9w==", + "version": "4.0.65", + "resolved": "https://registry.npmjs.org/@defillama/sdk/-/sdk-4.0.65.tgz", + "integrity": "sha512-+KCjkA+Ytqhre8PoFHhdUmzTYUtimJUmeYwnpnLR9xs3CcgeqyTxEt91Ab8aIjcXf2AvAUPcqBEEcp/MEczNzQ==", "dependencies": { "@aws-sdk/client-s3": "^3.400.0", "@supercharge/promise-pool": "^2.1.0", @@ -3260,6 +3262,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/curve25519-js": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/curve25519-js/-/curve25519-js-0.0.4.tgz", + "integrity": "sha512-axn2UMEnkhyDUPWOwVKBMVIzSQy2ejH2xRGy1wq81dqRwApXfIzfbE3hIX0ZRFBIihf/KDqK158DLwESu4AK1w==" + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -4409,6 +4416,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/miscreant": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/miscreant/-/miscreant-0.3.2.tgz", + "integrity": "sha512-fL9KxsQz9BJB2KGPMHFrReioywkiomBiuaLk6EuChijK0BsJsIKJXdVomR+/bPj5mvbFD6wM0CM3bZio9g7OHA==" + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -5905,9 +5917,9 @@ } }, "@defillama/sdk": { - "version": "4.0.64", - "resolved": "https://registry.npmjs.org/@defillama/sdk/-/sdk-4.0.64.tgz", - "integrity": "sha512-wqM4enhx11vN5OuRNpK9QMoOEQbbQFKu7tT0rM6ddYA4w1tvuayWAn8mjfV8TN5wd88aHV4+zNY0Sps+mrVN9w==", + "version": "4.0.65", + "resolved": "https://registry.npmjs.org/@defillama/sdk/-/sdk-4.0.65.tgz", + "integrity": "sha512-+KCjkA+Ytqhre8PoFHhdUmzTYUtimJUmeYwnpnLR9xs3CcgeqyTxEt91Ab8aIjcXf2AvAUPcqBEEcp/MEczNzQ==", "requires": { "@aws-sdk/client-s3": "^3.400.0", "@supercharge/promise-pool": "^2.1.0", @@ -7656,6 +7668,11 @@ "resolved": "https://registry.npmjs.org/crypto-hash/-/crypto-hash-1.3.0.tgz", "integrity": "sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg==" }, + "curve25519-js": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/curve25519-js/-/curve25519-js-0.0.4.tgz", + "integrity": "sha512-axn2UMEnkhyDUPWOwVKBMVIzSQy2ejH2xRGy1wq81dqRwApXfIzfbE3hIX0ZRFBIihf/KDqK158DLwESu4AK1w==" + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -8535,6 +8552,11 @@ "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", "peer": true }, + "miscreant": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/miscreant/-/miscreant-0.3.2.tgz", + "integrity": "sha512-fL9KxsQz9BJB2KGPMHFrReioywkiomBiuaLk6EuChijK0BsJsIKJXdVomR+/bPj5mvbFD6wM0CM3bZio9g7OHA==" + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", diff --git a/package.json b/package.json index d3d9dbc281..7ddf601793 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "blakejs": "^1.2.1", "bn.js": "^5.2.1", "borsh": "^0.7.0", + "curve25519-js": "^0.0.4", "dotenv": "^8.6.0", "ethers": "^5.6.5", "graphql": "^16.6.0", @@ -35,6 +36,7 @@ "hi-base32": "^0.5.1", "js-sha512": "^0.8.0", "limiter": "2.1.0", + "miscreant": "^0.3.2", "p-limit": "^3.1.0", "starknet": "^4.17.1", "tron-format-address": "^0.1.8", diff --git a/projects/helper/CosmWasm.js b/projects/helper/CosmWasm.js deleted file mode 100644 index 6d069d6d50..0000000000 --- a/projects/helper/CosmWasm.js +++ /dev/null @@ -1,60 +0,0 @@ -const { get } = require('./http') -const EnigmaUtils = require('./utils/enigma') -const { toBase64, fromBase64, toUtf8, fromUtf8, toHex, } = EnigmaUtils - -class CosmWasmClient { - constructor(nodeURL) { - this.nodeURL = nodeURL - this.codeHashCache = {} - this.enigmautils = new EnigmaUtils(nodeURL) - } - - async getHeight() { - const latest = await this.get('/blocks/latest') - return parseInt(latest.block.header.height, 10) - } - - async queryContractSmart(contractAddress, query, addedParams) { - const contractCodeHash = await this.getCodeHashByContractAddr(contractAddress); - const encrypted = await this.enigmautils.encrypt(contractCodeHash, query); - const nonce = encrypted.slice(0, 32); - const encoded = toHex(toUtf8(toBase64(encrypted))); - // @ts-ignore - const paramString = new URLSearchParams(addedParams).toString(); - const path = `/wasm/contract/${contractAddress}/query/${encoded}?encoding=hex&${paramString}`; - let responseData = (await this.get(path)) - // By convention, smart queries must return a valid JSON document (see https://github.com/CosmWasm/cosmwasm/issues/144) - return JSON.parse(fromUtf8(fromBase64(fromUtf8(await this.enigmautils.decrypt(fromBase64(responseData.result.smart), nonce))))); - - } - - async get(api) { - return get(this.nodeURL + api) - } - - async getContracts(codeId) { - const path = `/wasm/code/${codeId}/contracts`; - const responseData = (await this.get(path)); - const result = responseData.result || []; - return result.map((entry) => ({ - address: entry.address, - codeId: entry.code_id, - creator: entry.creator, - label: entry.label, - })); - } - - async getCodeHashByContractAddr(addr) { - const codeHashFromCache = this.codeHashCache[addr]; - if (typeof codeHashFromCache === "string") - return codeHashFromCache; - - const path = `/wasm/contract/${addr}/code-hash`; - const responseData = await this.get(path); - this.codeHashCache[addr] = responseData.result - return responseData.result; - } - -} - -module.exports = CosmWasmClient \ No newline at end of file diff --git a/projects/helper/chain/secret.js b/projects/helper/chain/secret.js new file mode 100644 index 0000000000..80ff672c96 --- /dev/null +++ b/projects/helper/chain/secret.js @@ -0,0 +1,72 @@ +const { endPoints } = require('./cosmos'); +const EnigmaUtils = require('../utils/enigma') +const host = endPoints.secret +const { toBase64, fromBase64, fromUtf8, } = EnigmaUtils +const axios = require('axios') +let client + +function getClient() { + if (!client) client = new SecretClient(host) + return client +} + +async function queryContract({ contract, data, } = {}) { + return getClient().queryContractSmart({contract, data,}) +} +async function getContracts(codeId) { + return getClient().getContracts(codeId) +} + + +module.exports = { + queryContract, + getContracts, +} + +class SecretClient { + constructor(nodeURL) { + this.nodeURL = nodeURL + this.codeHashes = {} + this.enigmautils = new EnigmaUtils(nodeURL) + } + + async getContracts(codeId) { + console.log('getContracts', codeId) + const path = `/compute/v1beta1/contracts/${codeId}`; + const { contract_infos } = (await this.get(path)); + return contract_infos.map(({ contract_address, ContractInfo: { code_id, creator, label } }) => ({ + address: contract_address, + code_id, creator, label, + })); + } + + async queryContractSmart({ contract, data }) { + const contractCodeHash = await this.getCodeHashByContractAddr(contract); + const encrypted = await this.enigmautils.encrypt(contractCodeHash, data); + const nonce = encrypted.slice(0, 32); + const encoded = toBase64(encrypted); + const path = `/compute/v1beta1/query/${contract}`; + let responseData = (await this.get(path, { query: encoded })).data + // By convention, smart queries must return a valid JSON document (see https://github.com/CosmWasm/cosmwasm/issues/144) + return JSON.parse(fromUtf8(fromBase64(fromUtf8(await this.enigmautils.decrypt(fromBase64(responseData), nonce))))); + + } + + async get(api, queryParams = {}) { + const { data } = await axios.get(this.nodeURL + api, { params: queryParams }) + return data + } + + async getCodeHashByContractAddr(contract) { + if (!this.codeHashes[contract]) + this.codeHashes[contract] = fetchCodeHash(this) + + return this.codeHashes[contract] + + async function fetchCodeHash(obj) { + const { code_hash } = await obj.get(`/compute/v1beta1/code_hash/by_contract_address/${contract}`) + return code_hash + } + } + +} \ No newline at end of file diff --git a/projects/helper/utils/enigma.js b/projects/helper/utils/enigma.js index 4a1d1d8c22..aa50b5e053 100644 --- a/projects/helper/utils/enigma.js +++ b/projects/helper/utils/enigma.js @@ -46,6 +46,7 @@ class EnigmaUtils { // const txEncryptionIkm = curve25519.sharedKey(this.privkey, consensusIoPubKey); // const txEncryptionKey = crypto.hkdfSync("sha256", Uint8Array.from([...txEncryptionIkm, ...nonce]), hkdfSalt, '', 32) // const { key: txEncryptionKey } = await hkdf.compute(Uint8Array.from([...txEncryptionIkm, ...nonce]), "SHA-256", 32, "", hkdfSalt) + // console.log(txEncryptionKey, 'txEncryptionKey', new Uint8Array(txEncryptionKey)) const txEncryptionKey = fakeKey return new Uint8Array(txEncryptionKey) } @@ -115,4 +116,4 @@ EnigmaUtils.toBase64 = toBase64 module.exports = EnigmaUtils -const fakeKey = new Uint8Array([104, 52, 184, 163, 49, 137, 219, 71, 54, 99, 223, 160, 48, 83, 58, 92, 175, 118, 60, 103, 134, 11, 44, 191, 210, 206, 164, 120, 48, 100, 9, 130]).buffer \ No newline at end of file +const fakeKey = new Uint8Array([248, 24, 153, 160, 20, 71, 22, 226, 185, 239, 57, 17, 11, 65, 67, 231, 36, 199, 102, 223, 164, 45, 133, 137, 223, 33, 119, 169, 155, 169, 194, 224]).buffer \ No newline at end of file diff --git a/projects/sienna-lend/index.js b/projects/sienna-lend/index.js new file mode 100644 index 0000000000..4df3aec406 --- /dev/null +++ b/projects/sienna-lend/index.js @@ -0,0 +1,94 @@ +const { queryContract, } = require('../helper/chain/secret') +const { PromisePool } = require('@supercharge/promise-pool') + +const LEND_OVERSEER_CONTRACT = "secret1pf88n9hm64mn58aw48jxfs2fsvzr07svnrrdlv"; + +let lendingMarkets = null + +async function getLendMarkets() { + if (!lendingMarkets) lendingMarkets = _get() + return lendingMarkets + + async function _get() { + let markets = [], hasMore = true, start = 0, limit = 30 + + while (hasMore) { + const { entries, total } = await queryContract({ + contract: LEND_OVERSEER_CONTRACT, data: { + markets: { + pagination: { + limit: 30, + start + } + } + } + }); + start += limit + hasMore = total > start + markets = markets.concat(entries.map(i => i.contract.address)); + } + + const data = [] + + const { errors } = await PromisePool.withConcurrency(5) + .for(markets) + .process(async (addr) => { + + let { total_borrows, total_supply, } = await queryContract({ contract: addr, data: { state: {} } }) + let exchange_rate = await queryContract({ contract: addr, data: { exchange_rate: {} } }) + const { address } = await queryContract({ contract: addr, data: { underlying_asset: {} } }) + // const { token_info: { decimals }} = await queryContract({ contract: address, data: { token_info: {} } }) + const scale = exchange_rate + data.push({ address, total_borrows: total_borrows * scale, total_supply: total_supply * scale }) + }) + + if (errors && errors.length) + throw errors[0] + + return data + } + +} + +async function tvl(_, _b, _cb, { api, }) { + const data = await getLendMarkets() + data.forEach(i => { + api.add(i.address, i.total_supply - i.total_borrows) + }) + + return api.getBalances() +} + +async function borrowed(_, _b, _cb, { api, }) { + const data = await getLendMarkets() + data.forEach(i => { + api.add(i.address, i.total_borrows) + }) + + + return api.getBalances() +} + +async function staking(_, _b, _cb, { api, }) { + const SIENNA_SINGLE_SIDED_POOLS = [ + { address: "secret1ja57vrpqusx99rgvxacvej3vhzhh4rhlkdkd7w", version: 1 }, + { address: "secret109g22wm3q3nfys0v6uh7lqg68cn6244n2he4t6", version: 2 }, + { address: "secret1uta9zf3prn7lvc6whp8sqv7ynxmtz3jz9xkyu7", version: 3 } + ]; + + const SIENNA_TOKEN_ADDRESS = "secret1rgm2m5t530tdzyd99775n6vzumxa5luxcllml4"; + await Promise.all(SIENNA_SINGLE_SIDED_POOLS.map(async ({ address, version }) => { + if (version === 3) { + const fetchedPool = await queryContract({ contract: address, data: { rewards: { pool_info: { at: new Date().getTime() } } } }); + api.add('sienna', fetchedPool.rewards.pool_info.staked / 1e18, { skipChain: true }) + } else { + const fetchedPool = await queryContract({ contract: address, data: { pool_info: { at: new Date().getTime() } } }); + api.add('sienna', fetchedPool.pool_info.pool_locked / 1e18, { skipChain: true }) + } + })); + return api.getBalances() +} + +module.exports = { + secret: { tvl, borrowed, staking, } +} \ No newline at end of file diff --git a/projects/sienna/index.js b/projects/sienna/index.js index c596100cc1..b40c3f16b5 100644 --- a/projects/sienna/index.js +++ b/projects/sienna/index.js @@ -1,56 +1,55 @@ -const { get } = require('../helper/http') - -let lendData -let overviewData - -async function getLendData() { - if (!lendData) - lendData = get('https://ethereumbridgebackend.azurewebsites.net/sienna_lend_latest_data') - return lendData -} - -async function getOverviewData() { - if (!overviewData) - overviewData = get('https://ethereumbridgebackend.azurewebsites.net/sienna_token_statistics') - return overviewData -} - -async function getPoolLiquidity() { - let pool_liquidity - const { data } = await get('https://ethereumbridgebackend.azurewebsites.net/sienna_token_historical_data?type=hourly&period=1+days') - data.filter(i => i.pool_liquidity).forEach(i => pool_liquidity = i.pool_liquidity) - return pool_liquidity +const { queryContract, } = require('../helper/chain/secret') +const { transformDexBalances } = require('../helper/portedTokens') +const { PromisePool } = require('@supercharge/promise-pool') +const sdk = require('@defillama/sdk') + +async function tvl(_, _b, _cb, { api, }) { + const factiories = ["secret18sq0ux28kt2z7dlze2mu57d3ua0u5ayzwp6v2r", "secret1zvk7pvhtme6j8yw3ryv0jdtgg937w0g0ggu8yy"] + const data = [] + await Promise.all(factiories.map(i => getExchanges(i, data))) + return transformDexBalances({ data, chain: api.chain }) } -async function tvl() { - throw new Error('tvlresponse is wrong') - // const [ lend, overview ] = await Promise.all([getLendData(), getOverviewData()]) - // return { - // tether: lend.data.underlying_balance_usd + (await getPoolLiquidity()) - // } +async function getExchanges(factory, data) { + const pools = [] + const limit = 30 + let hasMore = true + do { + const { list_exchanges: { exchanges } } = await queryContract({ contract: factory, data: { list_exchanges: { pagination: { start: pools.length, limit } } } }) + hasMore = exchanges.length === limit + + sdk.log(factory, exchanges.length, pools.length, hasMore) + pools.push(...exchanges) + const { errors } = await PromisePool.withConcurrency(3) + .for(exchanges) + .process(async (i) => { + let { address, contract } = i + if (!address) address = contract.address + const { pair_info } = await queryContract({ contract: address, data: "pair_info" }) + data.push({ + token0: transformToken(pair_info.pair.token_0, pair_info), + token0Bal: pair_info.amount_0, + token1: transformToken(pair_info.pair.token_1, pair_info), + token1Bal: pair_info.amount_1, + }) + }) + + if (errors && errors.length) + throw errors[0] + + } while (hasMore) } -async function staking() { - const overview = await getOverviewData() - return { - tether: overview.staked - } -} - -async function borrowed() { - const lend = await getLendData() - return { - tether: lend.data.total_borrows_usd +function transformToken(addr, poolData) { + if (!addr?.custom_token) { + console.log(JSON.stringify(addr, null, 2)) + console.log(JSON.stringify(poolData, null, 2)) + throw new Error("No custom token") } + return addr.custom_token.contract_addr } module.exports = { - misrepresentedTokens: true, timetravel: false, - methodology: 'All tokens locked in SIENNA Network pairs + All the supplied tokens to Sienna Lend Markets + Staked Sienna;', - secret: { - tvl, - staking, - borrowed, - } + secret: { tvl } } \ No newline at end of file diff --git a/projects/sienna/utils.js b/projects/sienna/utils.js deleted file mode 100644 index 5f9b59f92d..0000000000 --- a/projects/sienna/utils.js +++ /dev/null @@ -1,72 +0,0 @@ -module.exports = { - symbolsMap: { - "ALPHA": "alpha-finance", - "ALTER": "alter", - "BAT": "basic-attention-token", - "BUTT": "buttcoin-2", - "ENJ": "enjincoin", - "MANA": "decentraland", - "MATIC": "matic-network", - "RUNE": "thorchain-erc20", - "SAAVE": "aave", - "SADA(BSC)": "cardano", - "SATOM": "cosmos", - "SBAC": "basis-cash", - "SBAKE": "bakerytoken", - "SBAND": "band-protocol", - "SBCH(BSC)": "bitcoin-cash", - "SBNB(BSC)": "binancecoin", - "SBUNNY": "pancake-bunny", - "SBUSD(BSC)": "binance-usd", - "SCAKE": "pancakeswap-token", - "SCOMP": "compound-governance-token", - "SDAI": "dai", - "SDOGE(BSC)": "dogecoin", - "SDOT(BSC)": "polkadot", - "SDOT": "polkadot", - "SDPI": "defipulse-index", - "SDVPN": "sentinel", - "SEFI": "secret-finance", - "SETH(BSC)": "ethereum", - "SETH": "ethereum", - "SFINE": "refinable", - "SHD": "shade-protocol", - "SHUAHUA": "shuahua", - "SIENNA": "sienna", - "SJUNO": "juno-network", - "SKNC": "kyber-network", - "SLINA": "linear", - "SLINK(BSC)": "chainlink", - "SLINK": "chainlink", - "SLTC(BSC)": "litecoin", - "SLTC": "litecoin", - "SLUNA": "terra-luna", - "SMKR": "maker", - "SOCEAN": "ocean-protocol", - "SOSMO": "osmosis", - "SREN": "republic-protocol", - "SRNBTC": "renbtc", - "SRSR": "reserve-rights-token", - "SSCRT(BSC)": "secret", - "SSCRT": "secret", - "SSNX": "havven", - "SSUSHI": "sushi", - "STRX(BSC)": "tron", - "STUSD": "true-usd", - "SUNI": "uniswap", - "SUSDC(BSC)": "usd-coin", - "SUSDC": "usd-coin", - "SUSDT(BSC)": "tether", - "SUSDT": "tether", - "SUST": "terrausd", - "SWBTC": "wrapped-bitcoin", - "SXMR": "monero", - "SXRP(BSC)": "ripple", - "SXVS": "venus", - "SYFI": "yearn-finance", - "TORN": "tornado-cash", - "YFL": "yflink", - "ZRX": "0x", - "stkd-SCRT": "stkd-scrt" - } -} \ No newline at end of file