Skip to content

Commit a5aa01b

Browse files
authored
Merge pull request #224 from r4topunk/fix/orbit-official-stakes-only
fix(stake): órbita conta só stakes oficiais (receiver = split)
2 parents 35a2f19 + fd66bcf commit a5aa01b

1 file changed

Lines changed: 32 additions & 1 deletion

File tree

src/services/stake-graph.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,30 @@ async function etherscanReferred(pool: Address, referrer: Address, key: string):
170170
return [];
171171
}
172172

173+
// The MOR claim receiver a staker set for a pool (0x0 if never set). The /stake
174+
// flow wires this to the rider's 3-way split, so a NON-zero receiver marks an
175+
// "official" sponsorship stake (its MOR routes to Gnars + the athlete). A zero
176+
// receiver is a raw Morpheus deposit that pays 100% back to the staker — not a
177+
// sponsorship, so the orbit must not count it. Read via Etherscan proxy eth_call
178+
// (datacenter-safe, same key as the log scan).
179+
const CLAIM_RECEIVER_SIG = keccak256(toHex("claimReceiver(uint256,address)")).slice(0, 10);
180+
async function etherscanClaimReceiver(pool: Address, user: Address, key: string): Promise<Address | null> {
181+
const data = `${CLAIM_RECEIVER_SIG}${"0".repeat(64)}${pad32(user).slice(2)}`;
182+
const url =
183+
`https://api.etherscan.io/v2/api?chainid=1&module=proxy&action=eth_call` +
184+
`&to=${pool}&data=${data}&tag=latest&apikey=${key}`;
185+
for (let i = 0; i < 4; i++) {
186+
try {
187+
const res = await fetch(url, { cache: "no-store" });
188+
const j = (await res.json()) as { result?: unknown; message?: string };
189+
if (typeof j.result === "string" && j.result.length >= 66) return getAddress(`0x${j.result.slice(-40)}`);
190+
if (/rate limit/i.test(String(j.result)) || /rate limit/i.test(String(j.message))) { await sleep(600); continue; }
191+
return null;
192+
} catch { await sleep(300); }
193+
}
194+
return null;
195+
}
196+
173197
async function morBackersByRider(ethUsd: number): Promise<Record<string, OrbitBacker[]>> {
174198
const walletToId = new Map<string, RiderId>();
175199
for (const r of RIDER_LIST) if (r.wallet) walletToId.set(r.wallet.toLowerCase(), r.id);
@@ -201,9 +225,16 @@ async function morBackersByRider(ethUsd: number): Promise<Record<string, OrbitBa
201225
for (const l of logs) byUser.set(l.user.toLowerCase(), (byUser.get(l.user.toLowerCase()) ?? BigInt(0)) + l.amount);
202226
const out: Array<{ id: RiderId; backer: OrbitBacker }> = [];
203227
for (const [userLc, amt] of byUser) {
228+
const user = getAddress(userLc);
229+
// Only OFFICIAL /stake deposits belong in the sponsorship orbit: the
230+
// site wires the MOR claim receiver to the rider's 3-way split. A zero
231+
// receiver is a raw Morpheus stake whose MOR pays 100% to the staker
232+
// (no Gnars/athlete cut) — exclude it from the orbit AND the total.
233+
const receiver = await etherscanClaimReceiver(pool, user, ETHERSCAN_KEY as string);
234+
if (!receiver || receiver.toLowerCase() === ZERO) continue;
204235
const tokens = Number(formatUnits(amt, decimals));
205236
const usd = asset === "steth" ? tokens * ethUsd : tokens;
206-
if (usd > 0) out.push({ id, backer: { address: getAddress(userLc), amount: usd, kind: "mor", asset } });
237+
if (usd > 0) out.push({ id, backer: { address: user, amount: usd, kind: "mor", asset } });
207238
}
208239
return out;
209240
}),

0 commit comments

Comments
 (0)