-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Add Gifted Markets volume adapter #5788
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
base: master
Are you sure you want to change the base?
Changes from 16 commits
c179374
496a3a5
60d1614
3de7b6b
f426721
ea850d0
b4b47d0
6c88635
ce6ee4f
c3880d4
a3618e5
8cfec0b
62d7764
8c93c45
9ecdae1
b272af5
e577b5a
a8e0bae
543d604
881c7a1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| import { FetchOptions, SimpleAdapter } from "../../adapters/types"; | ||
| import { CHAIN } from "../../helpers/chains"; | ||
| import { httpGet } from "../../utils/fetchURL"; | ||
|
|
||
| const PERPS_FEE_WALLET = "EQAO6fsGZMl8PEAOIeW5-xRBw7tt9fwrLqIijmyPifGwA9lR"; | ||
| const TON_API = "https://tonapi.io/v2"; | ||
|
|
||
| const fetchVolume = async (options: FetchOptions) => { | ||
| const { startTimestamp, endTimestamp } = options; | ||
|
|
||
| const ratesResponse = await httpGet(`${TON_API}/rates?tokens=ton¤cies=usd`); | ||
| const tonPriceUSD = ratesResponse.rates?.TON?.prices?.USD || 3.50; | ||
|
|
||
| const txResponse = await httpGet(`${TON_API}/blockchain/accounts/${PERPS_FEE_WALLET}/transactions?limit=1000`); | ||
|
|
||
| let dailyVolumeNano = 0n; | ||
|
|
||
| for (const tx of txResponse.transactions || []) { | ||
| if (tx.utime < startTimestamp || tx.utime > endTimestamp) continue; | ||
| if (!tx.success) continue; | ||
| if (tx.in_msg?.value > 0) { | ||
| dailyVolumeNano += BigInt(tx.in_msg.value) * 100n; | ||
| } | ||
| } | ||
|
|
||
| const dailyVolumeUSD = (Number(dailyVolumeNano) / 1e9) * tonPriceUSD; | ||
|
Comment on lines
+16
to
+26
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: JavaScript That’s why converting a const b = 9007199254740993n; // MAX_SAFE_INTEGER + 2
Number(b); // 9007199254740992 (rounded)MDN explicitly warns that coercing Practical rules:
Citations:
🌐 Web query:
💡 Result:
Example Number.MAX_SAFE_INTEGER; // 9007199254740991
Number.MAX_SAFE_INTEGER + 1 ===
Number.MAX_SAFE_INTEGER + 2; // true (precision loss)
const x = BigInt(Number.MAX_SAFE_INTEGER);
x + 1n === x + 2n; // false (exact)Sources: 🏁 Script executed: cd aggregator-derivatives/groypfi && head -50 index.tsRepository: DefiLlama/dimension-adapters Length of output: 1315 🏁 Script executed: rg -A 5 -B 5 "tx\.in_msg" aggregator-derivatives/groypfi/index.tsRepository: DefiLlama/dimension-adapters Length of output: 434 🏁 Script executed: rg "tx\." aggregator-derivatives/groypfi/ -t ts --max-count=20Repository: DefiLlama/dimension-adapters Length of output: 423 🏁 Script executed: find aggregator-derivatives -name "*.md" -o -name "*.txt" | head -10Repository: DefiLlama/dimension-adapters Length of output: 54 🏁 Script executed: rg "ton\|TON\|transaction\|value" aggregator-derivatives/groypfi/ -t ts -A 2 | head -50Repository: DefiLlama/dimension-adapters Length of output: 54 Guard against BigInt→Number precision loss in USD conversion. JavaScript's Apply the proposed fix to convert safely: Safe conversion pattern- const dailyVolumeUSD = (Number(dailyVolumeNano) / 1e9) * tonPriceUSD;
+ const NANO_PER_TON = 1_000_000_000n;
+ const wholeTons = dailyVolumeNano / NANO_PER_TON;
+ if (wholeTons > BigInt(Number.MAX_SAFE_INTEGER)) {
+ throw new Error(`dailyVolumeNano exceeds JS safe range: ${dailyVolumeNano}`);
+ }
+ const dailyVolumeTON =
+ Number(wholeTons) + Number(dailyVolumeNano % NANO_PER_TON) / 1e9;
+ const dailyVolumeUSD = dailyVolumeTON * tonPriceUSD;🤖 Prompt for AI Agents |
||
|
|
||
| return { dailyVolume: dailyVolumeUSD }; | ||
| }; | ||
|
|
||
| const adapter: SimpleAdapter = { | ||
| version: 2, | ||
| adapter: { | ||
| [CHAIN.TON]: { | ||
| fetch: fetchVolume, | ||
| start: 1735689600, | ||
| }, | ||
| }, | ||
| }; | ||
|
|
||
| export default adapter; | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,41 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { FetchOptions, SimpleAdapter } from "../../adapters/types"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { CHAIN } from "../../helpers/chains"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { httpGet } from "../../utils/fetchURL"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const FEE_WALLET = "EQAO6fsGZMl8PEAOIeW5-xRBw7tt9fwrLqIijmyPifGwA9lR"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const TON_API = "https://tonapi.io/v2"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const fetchVolume = async (options: FetchOptions) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { startTimestamp, endTimestamp } = options; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const ratesResponse = await httpGet(`${TON_API}/rates?tokens=ton¤cies=usd`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const tonPriceUSD = ratesResponse.rates?.TON?.prices?.USD || 3.50; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const txResponse = await httpGet(`${TON_API}/blockchain/accounts/${FEE_WALLET}/transactions?limit=1000`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let dailyVolumeNano = 0n; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (const tx of txResponse.transactions || []) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (tx.utime < startTimestamp || tx.utime > endTimestamp) continue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!tx.success) continue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (tx.in_msg?.value > 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dailyVolumeNano += BigInt(tx.in_msg.value) * 200n; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+14
to
+24
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: For TONAPI v2 Query params (pagination-related):
How to paginate (typical, newest → older):
Oldest → newest:
Source: TONAPI/OpenTonAPI OpenAPI spec for this endpoint. [1] Implement pagination for TONAPI transactions; If the address has more than 1,000 transactions in the time window, you'll miss volume. Loop through pages using TONAPI's LT-based pagination: retrieve the smallest 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const dailyVolumeUSD = (Number(dailyVolumeNano) / 1e9) * tonPriceUSD; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+16
to
+26
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # First, let's understand the file context and see what values tx.in_msg.value could be
cat -n bridge-aggregators/groypfi/index.tsRepository: DefiLlama/dimension-adapters Length of output: 1596 🏁 Script executed: # Search for how txResponse is obtained and what types are involved
rg -A 5 -B 5 "txResponse" bridge-aggregators/groypfi/index.tsRepository: DefiLlama/dimension-adapters Length of output: 675 🏁 Script executed: # Look for any type definitions or interfaces related to transactions
fd -e ts -e tsx bridge-aggregators/groypfi/ | head -20Repository: DefiLlama/dimension-adapters Length of output: 485 🌐 Web query:
💡 Result:
Example Number.MAX_SAFE_INTEGER; // 9007199254740991
Number.MAX_SAFE_INTEGER + 1 ===
Number.MAX_SAFE_INTEGER + 2; // true (precision loss) [1]
BigInt(Number.MAX_SAFE_INTEGER) + 2n; // 9007199254740993n
1n + 1; // TypeError (must convert) [4]Sources: [1] [2] [3] [4] Guard against silent BigInt→Number precision loss in USD conversion.
🔧 Proposed solution to ensure precision- const dailyVolumeUSD = (Number(dailyVolumeNano) / 1e9) * tonPriceUSD;
+ const NANO_PER_TON = 1_000_000_000n;
+ const wholeTons = dailyVolumeNano / NANO_PER_TON;
+ if (wholeTons > BigInt(Number.MAX_SAFE_INTEGER)) {
+ throw new Error(`dailyVolumeNano exceeds JS safe range: ${dailyVolumeNano}`);
+ }
+ const dailyVolumeTON =
+ Number(wholeTons) + Number(dailyVolumeNano % NANO_PER_TON) / 1e9;
+ const dailyVolumeUSD = dailyVolumeTON * tonPriceUSD;📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { dailyBridgeVolume: dailyVolumeUSD }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const adapter: SimpleAdapter = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| version: 2, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| adapter: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [CHAIN.TON]: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fetch: fetchVolume, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| start: 1735689600, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export default adapter; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| const axios = require("axios"); | ||
|
|
||
| const API_BASE = "https://api.gifted.markets"; | ||
|
|
||
| async function fetch(timestamp) { | ||
| const url = `${API_BASE}/defillama/volume`; | ||
| const r = await axios.get(url, { | ||
| params: { timestamp }, | ||
| timeout: 20_000, | ||
| }); | ||
|
|
||
| const dailyVolume = Number(r.data?.dailyVolumeUsd ?? 0); | ||
|
|
||
| if (!Number.isFinite(dailyVolume) || dailyVolume < 0) { | ||
| throw new Error(`Invalid dailyVolumeUsd from ${url}: ${r.data?.dailyVolumeUsd}`); | ||
| } | ||
|
|
||
| return { | ||
| dailyVolume, | ||
| }; | ||
| } | ||
|
|
||
| module.exports = { | ||
| timetravel: true, | ||
| start: 1769904000, // 2026-02-01 00:00:00 UTC | ||
| methodology: | ||
| "Daily volume is derived from Gifted Markets TON hot-wallet on-chain spend for completed purchases (excluding internal transfers/refunds) and converted to USD.", | ||
| fetch, | ||
| }; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,41 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { FetchOptions, SimpleAdapter } from "../../adapters/types"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { CHAIN } from "../../helpers/chains"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { httpGet } from "../../utils/fetchURL"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const SWAP_REFERRER = "UQDu4AiT__JKuqT0Znje0RoXIQMPcj4uIGYZme3UK4hFlE_Q"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const TON_API = "https://tonapi.io/v2"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const fetchVolume = async (options: FetchOptions) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { startTimestamp, endTimestamp } = options; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const ratesResponse = await httpGet(`${TON_API}/rates?tokens=ton¤cies=usd`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const tonPriceUSD = ratesResponse.rates?.TON?.prices?.USD || 3.50; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const txResponse = await httpGet(`${TON_API}/blockchain/accounts/${SWAP_REFERRER}/transactions?limit=1000`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not a optimal way, in case we want to backfill the data in future, fetching all transaction is not a good way to calculate this.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no need to hardcode or fetch TON Prices, you can simply use the balance object and add ton address and amount or use addCGToken helper in balance object |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let dailyVolumeNano = 0n; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (const tx of txResponse.transactions || []) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (tx.utime < startTimestamp || tx.utime > endTimestamp) continue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!tx.success) continue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (tx.in_msg?.value > 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dailyVolumeNano += BigInt(tx.in_msg.value) * 100n; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+14
to
+24
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: For TONAPI v2
Canonical reference is the official OpenAPI spec ( Citations:
Add pagination to TONAPI transactions; This will miss volume on days with >1,000 txs for the referrer wallet. Use the 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const dailyVolumeUSD = (Number(dailyVolumeNano) / 1e9) * tonPriceUSD; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+16
to
+26
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: find . -name "index.ts" -path "*/groypfi/*" | head -5Repository: DefiLlama/dimension-adapters Length of output: 199 🏁 Script executed: cat -n ./dexs/groypfi/index.ts | sed -n '1,50p'Repository: DefiLlama/dimension-adapters Length of output: 1596 🌐 Web query:
💡 Result:
Practical rule: if the value might exceed ±(2^53−1), keep it as Sources: Guard against BigInt→Number precision loss in USD conversion. The code converts Divide by the nanoTON scale before converting to Proposed fix- const dailyVolumeUSD = (Number(dailyVolumeNano) / 1e9) * tonPriceUSD;
+ const NANO_PER_TON = 1_000_000_000n;
+ const wholeTons = dailyVolumeNano / NANO_PER_TON;
+ if (wholeTons > BigInt(Number.MAX_SAFE_INTEGER)) {
+ throw new Error(`dailyVolumeNano exceeds JS safe range: ${dailyVolumeNano}`);
+ }
+ const dailyVolumeTON =
+ Number(wholeTons) + Number(dailyVolumeNano % NANO_PER_TON) / 1e9;
+ const dailyVolumeUSD = dailyVolumeTON * tonPriceUSD;📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { dailyVolume: dailyVolumeUSD }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const adapter: SimpleAdapter = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| version: 2, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| adapter: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [CHAIN.TON]: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fetch: fetchVolume, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| start: 1735689600, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export default adapter; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
TONAPI v2 /blockchain/accounts transactions pagination limit maximum💡 Result:
For TonAPI v2 endpoint:
GET /v2/blockchain/accounts/{account_id}/transactionslimitquery param hasminimum: 1,default: 100,maximum: 1000. So the max page size is 1000 transactions per request. [1]after_lt/before_lt(logical time cursors), and you can control ordering withsort_order(descdefault, orasc). [1]Source: TonAPI/OpenTonAPI OpenAPI spec (
openapi.yml). [1]Add pagination to TONAPI transactions query;
limit=1000may undercount daily volume.The TonAPI v2 endpoint supports pagination via
after_lt/before_ltlogical time cursors. The current code fetches onlylimit=1000without implementing pagination. If the fee wallet processes more than 1,000 transactions in a single day, the volume calculation will be incomplete. Implement cursor-based pagination, iterating through all transactions within the[startTimestamp, endTimestamp]range.🤖 Prompt for AI Agents