Skip to content

Add figure markets #15501

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
76 changes: 76 additions & 0 deletions projects/figure-markets-democratized-prime/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
const { mapTokens } = require("../helper/chain/provenance")
const { get } = require("../helper/http")

const tokenMapper = {
"HASH" : 'hash-2',
"LINK": 'chainlink',
"UNI": 'uniswap',
"BTC": 'bitcoin',
"ETH": 'ethereum',
"XRP": 'ripple',
"SOL": 'solana',
"LRWA": 'usd-coin',
"YLDS": 'uylds.fcc',
"USDC": 'usd-coin',
"USD": 'usd-coin',
"USDT": 'tether',
}

// Returns all leveraged pools in Figure Markets Democratized Prime
// https://www.figuremarkets.com/c/democratized-prime/lending-pools
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

leverage pools is derivatives trading? I wouldnt mix it with lending, also, how to get info of both on chain?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Working on this. I mislabeled that, it should have read lending pools.

const leveragePoolsUrl = 'https://www.figuremarkets.com/service-lending/api/v1/leverage-pools?location=US'

// Returns offer information for a specific asset
const offersUrl = (asset) => `https://www.figuremarkets.com/service-lending/api/v1/offers?asset=${asset}&location=US`

// Returns all assets, including lending facilities (specific to YLDS)
const lendingFacilities = `https://www.figuremarkets.com/service-hft-exchange/api/v1/assets?page=1&size=100&include_lending_facility_assets=true`

const getBalances = async () => {
const balances = {}
// Get all available pools
const pools = (await get(leveragePoolsUrl)).map(p => p.asset)
await Promise.all(pools.map(async p => {
if (p !== 'YLDS') {
// Get offers on each type that isn't YLDS
const details = (await get(offersUrl(p)))
// For collateral, subtracted the loan amount from the total amount
balances[p] = { collateral: Number(details.totalOfferAmount) - Number(details.totalLoanAmount), borrowed: details.totalLoanAmount }
} else {
// For YLDS, get all existing lending facility pools
const facilities = (await get(lendingFacilities)).data.filter(l => l.type === 'LENDING_FACILITY')
// Reduce existing pools into a single amount, which represents the total pool collateral in the protocol
const totalLendingFacilitiesValue = facilities.reduce((acc, cur) => acc += Number(cur.lendingFacilitiesDetails.unpaidBalance), 0)
// Also pull the existing YLDS loan amount
const borrowed = (await get(offersUrl(p))).totalLoanAmount
// Convert final YLDS to uylds (exponent of 6)
balances[p] = {collateral: (totalLendingFacilitiesValue - Number(borrowed)) * 1e6, borrowed: borrowed * 1e6 }
}
}))
return balances
}

const tvl = async (api) => {
const balances = await getBalances()
let collateral = {}
Object.keys(balances).map(b => collateral[tokenMapper[b]] = balances[b].collateral)
Object.keys(collateral).map(coin => mapTokens(collateral, coin, api ))
}

const borrowed = async (api) => {
const balances = (await getBalances())
let borrowed = {}
Object.keys(balances).map(b => borrowed[tokenMapper[b]] = balances[b].borrowed)
Object.keys(borrowed).map(coin => mapTokens(borrowed, coin, api ))
}

module.exports = {
timetravel: false,
doublecounted: true,
misrepresentedTokens: true,
methodology: 'Figure Markets Democratized Prime calculates the loan pool amount as TVL, with outstanding loans as the borrowed amount.',
provenance: {
tvl,
borrowed,
}
}
54 changes: 54 additions & 0 deletions projects/figure-markets/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
const { convertToCoinGeckoApiId, tokenMapping, mapTokens } = require("../helper/chain/provenance");
const { get } = require("../helper/http")

const paginationLimit = 1000;

const commitmentsQuery = (nextKey) =>
`https://api.provenance.io/provenance/exchange/v1/commitments?pagination.limit=${
paginationLimit
}${
nextKey ? `&pagination.key=${nextKey}` : ""
}`;

/**
* Tokens are committed to Provenance's Exchange Module in order to conduct
* decentralized exchanges. This functions accumulates a an object of committed
* denoms and the amount within the Provenance Exchange Module.
*/
const getCommittedTokens = async (acc, key) => {
// Retrieve all the commitments across all markets
const nextTokens = await get(commitmentsQuery(key));
// const nextTokens = await nextTokensRequest.json()
// Update the accumulator with each denom in the commitments
nextTokens.commitments.map((c) =>
c.amount.map((a) => {
const denom = a.denom;
if (acc[denom]) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be replaced with api.add(a.denom, a.amount)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, will push as soon as we answer the below question.

acc[denom] += Number(a.amount);
} else {
acc[denom] = Number(a.amount);
}
})
);
let nextKey = nextTokens.pagination.next_key;
if (nextKey) {
// convert base64 to URL-safe pagination key. We aren't using
// Buffer here because base64url removes padding.
nextKey = nextKey.replace(/\+/g, "-").replace(/\//g, "_");
return getCommittedTokens(acc, nextKey);
}
return acc;
};

const tvl = async (api) => {
const tokens = await getCommittedTokens({}, null)
await convertToCoinGeckoApiId(tokens)
Object.keys(tokens).map(coin => mapTokens(tokens, coin, api))
}

module.exports = {
timetravel: true,
misrepresentedTokens: true,
methodology: "Figure Markets TVL is calculated by the tokens committed in the Provenance Exchange module.",
provenance: { tvl },
}
2 changes: 2 additions & 0 deletions projects/helper/chain/cosmos.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ const endPoints = {
babylon: 'https://babylon-api.polkachu.com',
milkyway_rollup: 'https://archival-rest-moo-1.anvil.asia-southeast.initia.xyz',
titan: 'https://titan-lcd.titanlab.io',
provenance: 'https://api.provenance.io',
xion: 'https://api.xion-mainnet-1.burnt.com',
embr: 'https://rest-embrmainnet-1.anvil.asia-southeast.initia.xyz',
civitia: 'https://rest-civitia-1.anvil.asia-southeast.initia.xyz',
Expand All @@ -73,6 +74,7 @@ const endPoints = {
const chainSubpaths = {
crescent: "crescent",
osmosis: "osmosis",
provenance: 'provenance',
comdex: "comdex",
umee: "umee",
kava: "kava",
Expand Down
67 changes: 67 additions & 0 deletions projects/helper/chain/provenance.js
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this file can be removed

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed locally, will push once the below question is answered.

Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* Token Mapping for Provenance. Note that
* YLDS is pending on CoinGecko, but is a stablecoin.
* Additionally, LRWA and REIT are also 1:1 with USD
*/
const tokenMapping = {
hash: "hash-2",
"eth.figure.se": "ethereum",
"usd.trading": "usd-coin",
"usdc.figure.se": "usd-coin",
// "ylds.fcc": "ylds",
"usdt.figure.se": "tether",
"xrp.figure.se": "ripple",
"sol.figure.se": "solana",
"btc.figure.se": "bitcoin",
"uni.figure.se": "uniswap",
"lrwa.figure.markets": "usd-coin",
"link.figure.se": "chainlink",
'reit.figure.markets': "usd-coin",
};

/**
* Denoms are listed as their base unit. For example, HASH is returned as
* nhash, and needs to be converted to it's CoinGecko Value.
* We do this by querying the denom metadata and using the provided
* exponent and base unit to convert.
* @param tokenObject An object with key: token name (in base units)
* and value: amount of tokens
* Example: {
* nhash: 100000000
* }
*/
const convertToCoinGeckoApiId = async (tokenObject) =>
await Promise.all(
Object.keys(tokenObject).map(async (t) => {
// Get the denom exponent information
const response = await fetch(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, this is really bad, this will fetch denom metadata each time, use this structure instead: https://github.com/DefiLlama/DefiLlama-Adapters/blob/main/projects/helper/tokenMapping.js#L41

then you can do api.add(token, bal) in the adapter code and add

return sumTokens2({ api }), it will take care of transforming to cg tokens

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated locally, pushing after we answer the below question.

`https://api.provenance.io/cosmos/bank/v1beta1/denoms_metadata/${t}`
);
const denomExponent = (await response.json()).metadata?.denom_units[1]
// If a denom is present and we have mapped the token to the CoinGecko Equivalent...
if (denomExponent && tokenMapping[denomExponent.denom]) {
// Map these tokens to coingecko
const key = tokenMapping[denomExponent.denom]
if (tokenObject[key]) {
const incomingAmount = BigInt(tokenObject[t]) / BigInt(Math.pow(10, denomExponent.exponent))
const newVal = BigInt(tokenObject[key]) + incomingAmount
tokenObject[key] = newVal.toString();
} else {
tokenObject[key] = (BigInt(tokenObject[t]) / BigInt(Math.pow(10, denomExponent.exponent))).toString();
}
delete tokenObject[t];
}
})
);

// Map tokens. If they exist in coingecko, add to coingecko.
// Otherwise, keep in provenance
const mapTokens = (tokens, coin, api) =>
Object.values(tokenMapping).includes(coin) ?
api.addCGToken(coin, tokens[coin]) : api.add(coin, tokens[coin])

module.exports = {
tokenMapping,
convertToCoinGeckoApiId,
mapTokens,
}
1 change: 1 addition & 0 deletions projects/helper/chains.json
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@
"posi",
"prom",
"proton",
"provenance",
"pryzm",
"pulse",
"q",
Expand Down
2 changes: 1 addition & 1 deletion projects/helper/tokenMapping.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ coreAssets = JSON.parse(JSON.stringify(coreAssets))
// orbit brige: https://bridge.orbitchain.io/open/v1/api/monitor/rawTokenList


const ibcChains = ['ibc', 'terra', 'terra2', 'crescent', 'osmosis', 'kujira', 'stargaze', 'juno', 'injective', 'cosmos', 'comdex', 'umee', 'orai', 'persistence', 'fxcore', 'neutron', 'quasar', 'chihuahua', 'sei', 'archway', 'migaloo', 'secret', 'aura', 'xpla', 'bostrom', 'joltify', 'nibiru',
const ibcChains = ['ibc', 'terra', 'terra2', 'crescent', 'osmosis', 'kujira', 'stargaze', 'juno', 'injective', 'cosmos', 'provenance', 'comdex', 'umee', 'orai', 'persistence', 'fxcore', 'neutron', 'quasar', 'chihuahua', 'sei', 'archway', 'migaloo', 'secret', 'aura', 'xpla', 'bostrom', 'joltify', 'nibiru',
'kopi', 'elys', "pryzm", "mantra", 'agoric', 'band',
'celestia', 'dydx', 'carbon', 'milkyway', 'regen', 'sommelier', 'stride', 'prom', 'babylon', 'xion'
]
Expand Down
Loading