Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
266 changes: 266 additions & 0 deletions projects/shift-protocol/ShiftTvlFeed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
[
Copy link
Collaborator

Choose a reason for hiding this comment

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

please use string form ABI instead - try searching the repo for "function

Copy link
Contributor Author

@Gloom0x0 Gloom0x0 Oct 17, 2025

Choose a reason for hiding this comment

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

Noted

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Our TVL (currently) is defined as circulating liquid receipt tokens (ltPARA) * share price.

We emit ltPARA which is an ERC20 token on Base, against USDC deposits. The deposits then get transferred to Paradex and deposited in the Gigavault (which is their LP/MM vault).

Therefore, for the circulating of supply this is fairly easy, as it corresponds to https://basescan.org/token/0xaf69bf9ea9e0166498c0502af5b5945980ed1e0e

The price is calculated as a fundamental value via: underlying backing / number of circulating shares.

Therefore, as you see, our TVL is essentially calculated as the underlying backing.

The underlying backing is calculated via several steps:

The dust/inactive deposits on base at our wallet: https://basescan.org/address/0x2f8b5f84032c7ffea5a5dc990ce5eb7167d596ed

And the active deposits on Paradex.

Through Paradex APIs we get:

  • the amount of USDC on the Paradex account
  • the value of our deposit into the Gigavault (calculated as: number of Gigavault shares * price of one share, both extracted via Paradex APIs), with the numbers of gigavault shares we are holding being extracted through Starknet library

{
"inputs": [
{
"internalType": "address",
"name": "_accessControlContract",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "AlreadyInitialized",
"type": "error"
},
{
"inputs": [],
"name": "CountMustBePositive",
"type": "error"
},
{
"inputs": [],
"name": "IndexOutOfBounds",
"type": "error"
},
{
"inputs": [],
"name": "NotInitialized",
"type": "error"
},
{
"inputs": [
{
"internalType": "string",
"name": "_role",
"type": "string"
}
],
"name": "Unauthorized",
"type": "error"
},
{
"inputs": [],
"name": "ZeroAddress",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "newValue",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "timestamp",
"type": "uint256"
}
],
"name": "TvlUpdated",
"type": "event"
},
{
"inputs": [],
"name": "accessControlContract",
"outputs": [
{
"internalType": "contract IAccessControl",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "decimals",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getLastTvl",
"outputs": [
{
"components": [
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "timestamp",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "supplySnapshot",
"type": "uint256"
}
],
"internalType": "struct ShiftTvlFeed.TvlData",
"name": "",
"type": "tuple"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_count",
"type": "uint256"
}
],
"name": "getLastTvlEntries",
"outputs": [
{
"components": [
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "timestamp",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "supplySnapshot",
"type": "uint256"
}
],
"internalType": "struct ShiftTvlFeed.TvlData[]",
"name": "",
"type": "tuple[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_index",
"type": "uint256"
}
],
"name": "getTvlEntry",
"outputs": [
{
"components": [
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "timestamp",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "supplySnapshot",
"type": "uint256"
}
],
"internalType": "struct ShiftTvlFeed.TvlData",
"name": "",
"type": "tuple"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "init",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_shiftVaultContract",
"type": "address"
}
],
"name": "initialize",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "shiftVault",
"outputs": [
{
"internalType": "contract IShiftVault",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_value",
"type": "uint256"
}
],
"name": "updateTvl",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_user",
"type": "address"
},
{
"internalType": "uint256",
"name": "_value",
"type": "uint256"
}
],
"name": "updateTvlForDeposit",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]
5 changes: 5 additions & 0 deletions projects/shift-protocol/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
base: [
"0x6196810Be8e6a1D0fB0Ec10c789966c88340F19b",
],
};
38 changes: 38 additions & 0 deletions projects/shift-protocol/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const { getProvider } = require("@defillama/sdk/build/general");
const { Contract, formatUnits } = require("ethers");
const abi = require("./ShiftTvlFeed.json");
const contractsByChain = require("./config");

function getChainTvlFunction(chain) {
return async function tvl(_, _block, _chainBlocks) {
const provider = getProvider(chain);
let totalTvl = 0;

for (const address of contractsByChain[chain]) {
const contract = new Contract(address, abi, provider);
Copy link
Collaborator

Choose a reason for hiding this comment

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

can we use our SDK here rather than ethers?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If the defillama sdk can create contract interfaces, there is no issue. Beware of the abi that is custom for our contracts.

Copy link
Collaborator

Choose a reason for hiding this comment

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

please check the docs https://docs.llama.fi/list-your-project/submit-a-project and look at other adapters in the repo

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

const [tvlData, decimals] = await Promise.all([
contract.getLastTvl(),
contract.decimals()
]);

const tvlValue = parseFloat(formatUnits(tvlData.value, decimals));
totalTvl += tvlValue;
Copy link
Collaborator

Choose a reason for hiding this comment

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

please return token balances using api.add() or similar rather than exporting a USD amount

Copy link
Contributor Author

@Gloom0x0 Gloom0x0 Oct 17, 2025

Choose a reason for hiding this comment

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

This is not a token amout is the vault tvl trough our tvl oraclefeed.

Copy link
Collaborator

Choose a reason for hiding this comment

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

We need a way to count the tokens deposited to each vault

Copy link
Contributor Author

@Gloom0x0 Gloom0x0 Oct 17, 2025

Choose a reason for hiding this comment

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

Pivit to a different calculation for TVL with our share supply.

Copy link
Collaborator

Choose a reason for hiding this comment

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

This is still exporting a USD amount though - we need to export token balances of the deposit tokens

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So our situation right now is the following:

  • we track internally the USD TVL in the system
  • the way we do this is through Starknet's Library + Paradex API, to know how much Paradex Vault shares our wallet has and multiply that value per the current reported price of the share. The share has no open market, it's a fundamental price/value calculated based on the increasing amount of USD backing the shares.
  • afaik, there is no single way for us to surface the final result (how much TVL in $) without doing these two steps, as Paradex API by themselves don't offer this possibility, there is no "coingecko" or similar pricing for the underlying asset and no open markets to trade them.

We can share in detail how we calculate it / explain the methodology, would that be okay?

Copy link
Collaborator

Choose a reason for hiding this comment

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

yes please let us know the calculation

}

return {
usd: totalTvl
};
};
}

const adapter = {
methodology: "TVL is calculated as the aggregated amount of USDC deposited across all of Shift's vault, inclusive of losses or gains."
};

for (const chain of Object.keys(contractsByChain)) {
adapter[chain] = {
tvl: getChainTvlFunction(chain)
};
}

module.exports = adapter;
Loading