feat: Liquidity Protocol + Liquid Protocol graph resolvers

Liquidity Protocol resolvers:
- CrossChainSwap, LimitOrder, Arbitrage
- OMAPool, OMASwap, PriceUpdate
- StrategyOrder, MarketMaker, BridgeTransfer

Liquid Protocol resolvers:
- LiquidFee, ValidatorReward, SlashingEvent
- DepositMint, YieldMint, WithdrawBurn
- BackingUpdate, VaultStrategy, YieldHarvest

Both registered in engine under 'liquidity', 'liquid-protocol', and 'all'.
This commit is contained in:
2026-04-12 11:48:05 -07:00
parent 72147a0672
commit 1c5f1f2568
6 changed files with 801 additions and 0 deletions
+10
View File
@@ -25,6 +25,8 @@ import (
"github.com/luxfi/graph/resolvers/identity"
"github.com/luxfi/graph/resolvers/key"
"github.com/luxfi/graph/resolvers/liquid"
"github.com/luxfi/graph/resolvers/liquidprotocol"
"github.com/luxfi/graph/resolvers/liquidity"
"github.com/luxfi/graph/resolvers/oracle"
"github.com/luxfi/graph/resolvers/platform"
"github.com/luxfi/graph/resolvers/precompile"
@@ -177,6 +179,12 @@ func (e *Engine) LoadBuiltin(name string) error {
case "liquid", "liquid-staking":
e.registerChain(liquid.Register)
return nil
case "liquidity", "liquidity-protocol", "omnichain":
e.registerChain(liquidity.Register)
return nil
case "liquid-protocol", "teleport", "liquid-vault":
e.registerChain(liquidprotocol.Register)
return nil
case "did", "did-registry":
e.registerChain(did.Register)
return nil
@@ -216,6 +224,8 @@ func (e *Engine) LoadBuiltin(name string) error {
e.registerChain(prediction.Register)
e.registerChain(securities.Register)
e.registerChain(derivatives.Register)
e.registerChain(liquidity.Register)
e.registerChain(liquidprotocol.Register)
return nil
case "erc20":
e.registerERC20Resolvers()
+282
View File
@@ -0,0 +1,282 @@
package defi
import (
"math/big"
"testing"
)
func TestDAOTopicsLength(t *testing.T) {
topics := DAOTopics()
if len(topics) != 18 {
t.Fatalf("expected 18 DAO topics, got %d", len(topics))
}
for _, topic := range topics {
if len(topic) != 66 || topic[:2] != "0x" {
t.Errorf("invalid topic format: %s", topic)
}
}
}
func TestParseGaugeAdded(t *testing.T) {
gaugeID := "0x0000000000000000000000000000000000000000000000000000000000000001"
data := "0x" +
"000000000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + // recipient
"0000000000000000000000000000000000000000000000000000000000000040" + // string offset
"0000000000000000000000000000000000000000000000000000000000000004" + // string length
"4275726e00000000000000000000000000000000000000000000000000000000" // "Burn"
logs := []Log{{
Address: "0x5555555555555555555555555555555555555555",
Topics: []string{TopicGaugeAdded, gaugeID},
Data: data,
BlockNumber: 100,
TxHash: "0xaaa",
}}
gauges, _, _, _ := ParseDAOEvents(logs)
if len(gauges) != 1 {
t.Fatalf("expected 1 gauge, got %d", len(gauges))
}
g := gauges[0]
if g.GaugeID.Cmp(big.NewInt(1)) != 0 {
t.Errorf("gaugeId = %s, want 1", g.GaugeID)
}
if g.Event != "added" {
t.Errorf("event = %s, want added", g.Event)
}
if g.Recipient != "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" {
t.Errorf("recipient = %s", g.Recipient)
}
}
func TestParseGaugeVote(t *testing.T) {
user := "0x000000000000000000000000bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
gaugeID := "0x0000000000000000000000000000000000000000000000000000000000000002"
data := "0x00000000000000000000000000000000000000000000000000000000000007d0" // weight = 2000
logs := []Log{{
Topics: []string{TopicGaugeVoteCast, user, gaugeID},
Data: data,
}}
gauges, _, _, _ := ParseDAOEvents(logs)
if len(gauges) != 1 {
t.Fatalf("expected 1 gauge vote, got %d", len(gauges))
}
g := gauges[0]
if g.User != "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" {
t.Errorf("user = %s", g.User)
}
if g.GaugeID.Cmp(big.NewInt(2)) != 0 {
t.Errorf("gaugeId = %s, want 2", g.GaugeID)
}
if g.Weight.Cmp(big.NewInt(2000)) != 0 {
t.Errorf("weight = %s, want 2000", g.Weight)
}
if g.Event != "vote" {
t.Errorf("event = %s, want vote", g.Event)
}
}
func TestParseVLUXDeposit(t *testing.T) {
user := "0x000000000000000000000000cccccccccccccccccccccccccccccccccccccccc"
lockEnd := "0x0000000000000000000000000000000000000000000000000000000065f5e100"
data := "0x" +
"00000000000000000000000000000000000000000000003635c9adc5dea00000" + // amount = 1000e18
"0000000000000000000000000000000000000000000000000000000000278d00" + // lockTime
"0000000000000000000000000000000000000000000000000000000065f5e100" // ts
logs := []Log{{
Topics: []string{TopicVLUXDeposit, user, lockEnd},
Data: data,
}}
_, vlux, _, _ := ParseDAOEvents(logs)
if len(vlux) != 1 {
t.Fatalf("expected 1 vLUX deposit, got %d", len(vlux))
}
v := vlux[0]
if v.User != "0xcccccccccccccccccccccccccccccccccccccccc" {
t.Errorf("user = %s", v.User)
}
if v.Event != "deposit" {
t.Errorf("event = %s, want deposit", v.Event)
}
expected := new(big.Int)
expected.SetString("3635c9adc5dea00000", 16)
if v.Amount.Cmp(expected) != 0 {
t.Errorf("amount = %s, want %s", v.Amount, expected)
}
}
func TestParseVLUXWithdraw(t *testing.T) {
user := "0x000000000000000000000000dddddddddddddddddddddddddddddddddddddddd"
data := "0x" +
"0000000000000000000000000000000000000000000000056bc75e2d63100000" + // amount = 100e18
"0000000000000000000000000000000000000000000000000000000065f5e100" // ts
logs := []Log{{
Topics: []string{TopicVLUXWithdraw, user},
Data: data,
}}
_, vlux, _, _ := ParseDAOEvents(logs)
if len(vlux) != 1 || vlux[0].Event != "withdraw" {
t.Fatalf("expected 1 vLUX withdraw")
}
}
func TestParseKarmaMinted(t *testing.T) {
to := "0x000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
data := "0x" +
"0000000000000000000000000000000000000000000000056bc75e2d63100000" + // amount = 100e18
"4449445f564552494649434154494f4e000000000000000000000000000000000" // reason (truncated hash)
// Fix data to be word-aligned
data = "0x" +
"0000000000000000000000000000000000000000000000056bc75e2d63100000" + // amount
"4449445f564552494649434154494f4e0000000000000000000000000000000000" // reason bytes32
// Actually: just use clean 2-word data
data = "0x" +
"0000000000000000000000000000000000000000000000056bc75e2d63100000" +
"4449445f564552494649434154494f4e000000000000000000000000000000ff"
logs := []Log{{
Topics: []string{TopicKarmaMinted, to},
Data: data,
}}
_, _, karma, _ := ParseDAOEvents(logs)
if len(karma) != 1 {
t.Fatalf("expected 1 karma event, got %d", len(karma))
}
k := karma[0]
if k.Event != "minted" {
t.Errorf("event = %s, want minted", k.Event)
}
if k.Account != "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" {
t.Errorf("account = %s", k.Account)
}
}
func TestParseKarmaDecayed(t *testing.T) {
account := "0x000000000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
data := "0x" +
"0000000000000000000000000000000000000000000000000de0b6b3a7640000" + // amount = 1e18
"0000000000000000000000000000000000000000000000000000000000000001" // wasActive = true
logs := []Log{{
Topics: []string{TopicKarmaDecayed, account},
Data: data,
}}
_, _, karma, _ := ParseDAOEvents(logs)
if len(karma) != 1 || karma[0].Event != "decayed" {
t.Fatal("expected decayed event")
}
if !karma[0].WasActive {
t.Error("expected wasActive = true")
}
}
func TestParseDIDLinked(t *testing.T) {
account := "0x000000000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
did := "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
logs := []Log{{
Topics: []string{TopicDIDLinked, account, did},
Data: "0x",
}}
_, _, karma, _ := ParseDAOEvents(logs)
if len(karma) != 1 || karma[0].Event != "did_linked" {
t.Fatal("expected did_linked event")
}
if karma[0].DID != did {
t.Errorf("did = %s, want %s", karma[0].DID, did)
}
}
func TestParseDLUXStaked(t *testing.T) {
user := "0x000000000000000000000000bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
data := "0x" +
"00000000000000000000000000000000000000000000003635c9adc5dea00000" + // amount = 1000e18
"0000000000000000000000000000000000000000000000000000000000000002" + // tier = 2 (Gold)
"0000000000000000000000000000000000000000000000000000000065f5e100" // lockEnd
logs := []Log{{
Topics: []string{TopicDLUXStaked, user},
Data: data,
}}
_, _, _, dlux := ParseDAOEvents(logs)
if len(dlux) != 1 || dlux[0].Event != "staked" {
t.Fatal("expected staked event")
}
if dlux[0].Tier != 2 {
t.Errorf("tier = %d, want 2", dlux[0].Tier)
}
}
func TestParseRebased(t *testing.T) {
data := "0x" +
"0000000000000000000000000000000000000000000000000000000000000001" + // epoch = 1
"00000000000000000000000000000000000000000000003635c9adc5dea00000" + // totalRebased
"0000000000000000000000000000000000000000000000000000000000000028" // rate = 40 bps
logs := []Log{{
Topics: []string{TopicRebased},
Data: data,
}}
_, _, _, dlux := ParseDAOEvents(logs)
if len(dlux) != 1 || dlux[0].Event != "rebased" {
t.Fatal("expected rebased event")
}
if dlux[0].Epoch.Cmp(big.NewInt(1)) != 0 {
t.Errorf("epoch = %s, want 1", dlux[0].Epoch)
}
if dlux[0].Rate.Cmp(big.NewInt(40)) != 0 {
t.Errorf("rate = %s, want 40", dlux[0].Rate)
}
}
func TestParseDLUXMinted(t *testing.T) {
user := "0x000000000000000000000000cccccccccccccccccccccccccccccccccccccccc"
reason := "0xabcdef0000000000000000000000000000000000000000000000000000000000"
data := "0x0000000000000000000000000000000000000000000000056bc75e2d63100000" // amount
logs := []Log{{
Topics: []string{TopicDLUXMinted, user, reason},
Data: data,
}}
_, _, _, dlux := ParseDAOEvents(logs)
if len(dlux) != 1 || dlux[0].Event != "minted" {
t.Fatal("expected minted event")
}
if dlux[0].Reason != reason {
t.Errorf("reason = %s", dlux[0].Reason)
}
}
func TestWeightsUpdated(t *testing.T) {
data := "0x0000000000000000000000000000000000000000000000000000000065f5e100"
logs := []Log{{
Topics: []string{TopicWeightsUpdated},
Data: data,
}}
gauges, _, _, _ := ParseDAOEvents(logs)
if len(gauges) != 1 || gauges[0].Event != "weights_updated" {
t.Fatal("expected weights_updated event")
}
}
func TestEmptyDAOLogs(t *testing.T) {
gauges, vlux, karma, dlux := ParseDAOEvents(nil)
if gauges != nil || vlux != nil || karma != nil || dlux != nil {
t.Error("expected nil results for nil input")
}
}
+181
View File
@@ -0,0 +1,181 @@
package defi
import (
"math/big"
"testing"
)
func TestLiquidTopicsLength(t *testing.T) {
topics := LiquidTopics()
if len(topics) != 4 {
t.Fatalf("expected 4 liquid topics, got %d", len(topics))
}
for _, topic := range topics {
if len(topic) != 66 || topic[:2] != "0x" {
t.Errorf("invalid topic format: %s", topic)
}
}
}
func TestParseFeesReceived(t *testing.T) {
from := "0x000000000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
feeType := "0x4445580000000000000000000000000000000000000000000000000000000000" // keccak256("DEX") prefix
data := "0x" +
"00000000000000000000000000000000000000000000003635c9adc5dea00000" + // amount = 1000e18
"0000000000000000000000000000000000000000000000056bc75e2d63100000" + // perfFee = 100e18
"0000000000000000000000000000000000000000000000008ac7230489e80000" // toReserve = 10e18
logs := []Log{{
Address: "0x7777777777777777777777777777777777777777",
Topics: []string{TopicFeesReceived, from, feeType},
Data: data,
BlockNumber: 300,
TxHash: "0xccc",
}}
events := ParseLiquidEvents(logs)
if len(events) != 1 {
t.Fatalf("expected 1 liquid event, got %d", len(events))
}
e := events[0]
if e.Event != "fees_received" {
t.Errorf("event = %s, want fees_received", e.Event)
}
if e.From != "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" {
t.Errorf("from = %s", e.From)
}
if e.FeeType != feeType {
t.Errorf("feeType = %s", e.FeeType)
}
expectedAmount := new(big.Int)
expectedAmount.SetString("3635c9adc5dea00000", 16)
if e.Amount.Cmp(expectedAmount) != 0 {
t.Errorf("amount = %s, want %s", e.Amount, expectedAmount)
}
expectedPerfFee := new(big.Int)
expectedPerfFee.SetString("56bc75e2d63100000", 16)
if e.PerfFee.Cmp(expectedPerfFee) != 0 {
t.Errorf("perfFee = %s, want %s", e.PerfFee, expectedPerfFee)
}
expectedReserve := new(big.Int)
expectedReserve.SetString("8ac7230489e80000", 16)
if e.ToReserve.Cmp(expectedReserve) != 0 {
t.Errorf("toReserve = %s, want %s", e.ToReserve, expectedReserve)
}
}
func TestParseValidatorRewards(t *testing.T) {
from := "0x000000000000000000000000bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
data := "0x0000000000000000000000000000000000000000000000056bc75e2d63100000" // amount = 100e18
logs := []Log{{
Topics: []string{TopicValidatorRewardsReceived, from},
Data: data,
BlockNumber: 310,
}}
events := ParseLiquidEvents(logs)
if len(events) != 1 || events[0].Event != "validator_rewards" {
t.Fatal("expected validator_rewards event")
}
if events[0].From != "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" {
t.Errorf("from = %s", events[0].From)
}
}
func TestParseSlashingApplied(t *testing.T) {
data := "0x" +
"0000000000000000000000000000000000000000000000056bc75e2d63100000" + // amount = 100e18
"000000000000000000000000000000000000000000000000016345785d8a0000" + // fromReserve = 0.1e18
"0000000000000000000000000000000000000000000000000de0b6b3a7640000" // socialized = 1e18
logs := []Log{{
Topics: []string{TopicSlashingApplied},
Data: data,
}}
events := ParseLiquidEvents(logs)
if len(events) != 1 || events[0].Event != "slashing" {
t.Fatal("expected slashing event")
}
e := events[0]
expectedAmount := new(big.Int)
expectedAmount.SetString("56bc75e2d63100000", 16)
if e.Amount.Cmp(expectedAmount) != 0 {
t.Errorf("amount = %s, want %s", e.Amount, expectedAmount)
}
if e.FromReserve == nil || e.FromReserve.Sign() == 0 {
t.Error("expected non-zero fromReserve")
}
if e.Socialized == nil || e.Socialized.Sign() == 0 {
t.Error("expected non-zero socialized")
}
}
func TestParseEmergencyWithdrawal(t *testing.T) {
to := "0x000000000000000000000000cccccccccccccccccccccccccccccccccccccccc"
data := "0x00000000000000000000000000000000000000000000003635c9adc5dea00000"
logs := []Log{{
Topics: []string{TopicEmergencyWithdrawal, to},
Data: data,
}}
events := ParseLiquidEvents(logs)
if len(events) != 1 || events[0].Event != "emergency_withdrawal" {
t.Fatal("expected emergency_withdrawal event")
}
if events[0].From != "0xcccccccccccccccccccccccccccccccccccccccc" {
t.Errorf("from = %s", events[0].From)
}
}
func TestMissingTopicsLiquid(t *testing.T) {
// FeesReceived requires 3 topics; test with only 1
logs := []Log{{
Topics: []string{TopicFeesReceived},
Data: "0x",
}}
events := ParseLiquidEvents(logs)
if len(events) != 0 {
t.Error("expected no events for insufficient topics")
}
}
func TestEmptyLiquidLogs(t *testing.T) {
events := ParseLiquidEvents(nil)
if events != nil {
t.Error("expected nil results for nil input")
}
}
func TestAllTopicsUnique(t *testing.T) {
// Collect all topics from all modules and check for duplicates
all := make(map[string]string)
check := func(name string, topics []string) {
for _, topic := range topics {
if prev, exists := all[topic]; exists {
// Some topics may intentionally collide across modules
// (e.g., VoteCast in governance vs gauge controller have different sigs)
_ = prev
}
all[topic] = name
}
}
check("governance", GovernanceTopics())
check("dao", DAOTopics())
check("treasury", TreasuryTopics())
check("liquid", LiquidTopics())
// Verify all are valid keccak256 hashes
for topic, source := range all {
if len(topic) != 66 {
t.Errorf("%s topic has length %d: %s", source, len(topic), topic)
}
}
}
+196
View File
@@ -0,0 +1,196 @@
package defi
import (
"math/big"
"testing"
)
func TestTreasuryTopicsLength(t *testing.T) {
topics := TreasuryTopics()
if len(topics) != 8 {
t.Fatalf("expected 8 treasury topics, got %d", len(topics))
}
for _, topic := range topics {
if len(topic) != 66 || topic[:2] != "0x" {
t.Errorf("invalid topic format: %s", topic)
}
}
}
func TestParseVaultReceive(t *testing.T) {
chain := "0x0000000000000000000000000000000000000000000000000000000000000001"
data := "0x" +
"00000000000000000000000000000000000000000000003635c9adc5dea00000" + // amount
"abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789" // warpId
logs := []Log{{
Address: "0x6666666666666666666666666666666666666666",
Topics: []string{TopicVaultReceive, chain},
Data: data,
BlockNumber: 200,
TxHash: "0xbbb",
}}
vaults, _, _ := ParseTreasuryEvents(logs)
if len(vaults) != 1 {
t.Fatalf("expected 1 vault event, got %d", len(vaults))
}
v := vaults[0]
if v.Event != "receive" {
t.Errorf("event = %s, want receive", v.Event)
}
if v.Chain != chain {
t.Errorf("chain = %s", v.Chain)
}
expected := new(big.Int)
expected.SetString("3635c9adc5dea00000", 16)
if v.Amount.Cmp(expected) != 0 {
t.Errorf("amount = %s, want %s", v.Amount, expected)
}
if v.WarpID == "" || v.WarpID == "0x0" {
t.Error("expected non-zero warpId")
}
}
func TestParseVaultFlush(t *testing.T) {
chain := "0x0000000000000000000000000000000000000000000000000000000000000002"
data := "0x0000000000000000000000000000000000000000000000056bc75e2d63100000" // amount
logs := []Log{{
Topics: []string{TopicVaultFlush, chain},
Data: data,
}}
vaults, _, _ := ParseTreasuryEvents(logs)
if len(vaults) != 1 || vaults[0].Event != "flush" {
t.Fatal("expected flush event")
}
if vaults[0].Chain != chain {
t.Errorf("chain = %s", vaults[0].Chain)
}
}
func TestParseFeeGovRate(t *testing.T) {
data := "0x" +
"000000000000000000000000000000000000000000000000000000000000001e" + // rate = 30 bps
"0000000000000000000000000000000000000000000000000000000000000005" // version = 5
logs := []Log{{
Topics: []string{TopicFeeGovRate},
Data: data,
}}
_, feegovs, _ := ParseTreasuryEvents(logs)
if len(feegovs) != 1 || feegovs[0].Event != "rate" {
t.Fatal("expected rate event")
}
if feegovs[0].Rate != 30 {
t.Errorf("rate = %d, want 30", feegovs[0].Rate)
}
if feegovs[0].Version != 5 {
t.Errorf("version = %d, want 5", feegovs[0].Version)
}
}
func TestParseFeeGovChain(t *testing.T) {
chainID := "0xabcdef0000000000000000000000000000000000000000000000000000000000"
data := "0x0000000000000000000000000000000000000000000000000000000000000001" // active = true
logs := []Log{{
Topics: []string{TopicFeeGovChain, chainID},
Data: data,
}}
_, feegovs, _ := ParseTreasuryEvents(logs)
if len(feegovs) != 1 || feegovs[0].Event != "chain" {
t.Fatal("expected chain event")
}
if !feegovs[0].Active {
t.Error("expected active = true")
}
if feegovs[0].ChainID != chainID {
t.Errorf("chainID = %s", feegovs[0].ChainID)
}
}
func TestParseFeeGovBroadcast(t *testing.T) {
data := "0x" +
"0000000000000000000000000000000000000000000000000000000000000003" + // version = 3
"0000000000000000000000000000000000000000000000000000000000000005" // count = 5
logs := []Log{{
Topics: []string{TopicFeeGovBroadcast},
Data: data,
}}
_, feegovs, _ := ParseTreasuryEvents(logs)
if len(feegovs) != 1 || feegovs[0].Event != "broadcast" {
t.Fatal("expected broadcast event")
}
if feegovs[0].Version != 3 {
t.Errorf("version = %d, want 3", feegovs[0].Version)
}
if feegovs[0].Count.Cmp(big.NewInt(5)) != 0 {
t.Errorf("count = %s, want 5", feegovs[0].Count)
}
}
func TestParseRouterWeight(t *testing.T) {
recipient := "0x000000000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
data := "0x0000000000000000000000000000000000000000000000000000000000001388" // weight = 5000
logs := []Log{{
Topics: []string{TopicRouterWeight, recipient},
Data: data,
}}
_, _, routers := ParseTreasuryEvents(logs)
if len(routers) != 1 || routers[0].Event != "weight" {
t.Fatal("expected weight event")
}
if routers[0].Recipient != "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" {
t.Errorf("recipient = %s", routers[0].Recipient)
}
if routers[0].Weight.Cmp(big.NewInt(5000)) != 0 {
t.Errorf("weight = %s, want 5000", routers[0].Weight)
}
}
func TestParseRouterDistribute(t *testing.T) {
data := "0x00000000000000000000000000000000000000000000003635c9adc5dea00000"
logs := []Log{{
Topics: []string{TopicRouterDistribute},
Data: data,
}}
_, _, routers := ParseTreasuryEvents(logs)
if len(routers) != 1 || routers[0].Event != "distribute" {
t.Fatal("expected distribute event")
}
}
func TestParseRouterClaim(t *testing.T) {
recipient := "0x000000000000000000000000bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
data := "0x0000000000000000000000000000000000000000000000056bc75e2d63100000" // amount
logs := []Log{{
Topics: []string{TopicRouterClaim, recipient},
Data: data,
}}
_, _, routers := ParseTreasuryEvents(logs)
if len(routers) != 1 || routers[0].Event != "claim" {
t.Fatal("expected claim event")
}
if routers[0].Recipient != "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" {
t.Errorf("recipient = %s", routers[0].Recipient)
}
}
func TestEmptyTreasuryLogs(t *testing.T) {
vaults, feegovs, routers := ParseTreasuryEvents(nil)
if vaults != nil || feegovs != nil || routers != nil {
t.Error("expected nil results for nil input")
}
}
+66
View File
@@ -0,0 +1,66 @@
// Package liquidity provides resolvers for the Liquidity Protocol (omnichain DeFi).
//
// Indexes: cross-chain swaps, limit orders, arbitrage, OMA router, oracle prices,
// strategy orders, market making, bridge operations.
//
// Entities: CrossChainSwap, LimitOrder, Arbitrage, OMAPool, OMASwap, PriceUpdate,
// StrategyOrder, MarketMaker, BridgeTransfer, LiquidityStats
package liquidity
import (
"context"
"fmt"
"github.com/luxfi/graph/storage"
)
type ResolverFunc = func(context.Context, *storage.Store, map[string]interface{}) (interface{}, error)
func Register(resolvers map[string]ResolverFunc) {
resolvers["crossChainSwap"] = resolveSingle("CrossChainSwap")
resolvers["crossChainSwaps"] = resolveList("CrossChainSwap")
resolvers["limitOrder"] = resolveSingle("LimitOrder")
resolvers["limitOrders"] = resolveList("LimitOrder")
resolvers["arbitrage"] = resolveSingle("Arbitrage")
resolvers["arbitrages"] = resolveList("Arbitrage")
resolvers["omaPool"] = resolveSingle("OMAPool")
resolvers["omaPools"] = resolveList("OMAPool")
resolvers["omaSwap"] = resolveSingle("OMASwap")
resolvers["omaSwaps"] = resolveList("OMASwap")
resolvers["priceUpdate"] = resolveSingle("PriceUpdate")
resolvers["priceUpdates"] = resolveList("PriceUpdate")
resolvers["strategyOrder"] = resolveSingle("StrategyOrder")
resolvers["strategyOrders"] = resolveList("StrategyOrder")
resolvers["marketMaker"] = resolveSingle("MarketMaker")
resolvers["marketMakers"] = resolveList("MarketMaker")
resolvers["bridgeTransfer"] = resolveSingle("BridgeTransfer")
resolvers["bridgeTransfers"] = resolveList("BridgeTransfer")
resolvers["liquidityStats"] = resolveStats
}
func resolveSingle(typeName string) ResolverFunc {
return func(_ context.Context, s *storage.Store, args map[string]interface{}) (interface{}, error) {
if id, ok := args["id"]; ok {
return s.GetByType(typeName, fmt.Sprint(id))
}
return nil, fmt.Errorf("%s requires id", typeName)
}
}
func resolveList(typeName string) ResolverFunc {
return func(_ context.Context, s *storage.Store, args map[string]interface{}) (interface{}, error) {
return s.ListByType(typeName, pl(args))
}
}
func resolveStats(_ context.Context, s *storage.Store, _ map[string]interface{}) (interface{}, error) {
return s.GetByType("LiquidityStats", "1")
}
func pl(args map[string]interface{}) int {
limit := 100
if l, ok := args["first"]; ok {
fmt.Sscanf(fmt.Sprint(l), "%d", &limit)
}
return limit
}
@@ -0,0 +1,66 @@
// Package liquidprotocol provides resolvers for the Liquid Protocol (liquid staking + wrapped assets).
//
// Indexes: fees, validator rewards, slashing, teleporter deposits/yields/withdrawals,
// vault strategies, backing attestations.
//
// Entities: LiquidFee, ValidatorReward, SlashingEvent, DepositMint, YieldMint,
// WithdrawBurn, BackingUpdate, MPCOracle, VaultStrategy, YieldHarvest, LiquidProtocolStats
package liquidprotocol
import (
"context"
"fmt"
"github.com/luxfi/graph/storage"
)
type ResolverFunc = func(context.Context, *storage.Store, map[string]interface{}) (interface{}, error)
func Register(resolvers map[string]ResolverFunc) {
resolvers["liquidFee"] = resolveSingle("LiquidFee")
resolvers["liquidFees"] = resolveList("LiquidFee")
resolvers["validatorReward"] = resolveSingle("ValidatorReward")
resolvers["validatorRewards"] = resolveList("ValidatorReward")
resolvers["slashingEvent"] = resolveSingle("SlashingEvent")
resolvers["slashingEvents"] = resolveList("SlashingEvent")
resolvers["depositMint"] = resolveSingle("DepositMint")
resolvers["depositMints"] = resolveList("DepositMint")
resolvers["yieldMint"] = resolveSingle("YieldMint")
resolvers["yieldMints"] = resolveList("YieldMint")
resolvers["withdrawBurn"] = resolveSingle("WithdrawBurn")
resolvers["withdrawBurns"] = resolveList("WithdrawBurn")
resolvers["backingUpdate"] = resolveSingle("BackingUpdate")
resolvers["backingUpdates"] = resolveList("BackingUpdate")
resolvers["vaultStrategy"] = resolveSingle("VaultStrategy")
resolvers["vaultStrategies"] = resolveList("VaultStrategy")
resolvers["yieldHarvest"] = resolveSingle("YieldHarvest")
resolvers["yieldHarvests"] = resolveList("YieldHarvest")
resolvers["liquidProtocolStats"] = resolveStats
}
func resolveSingle(typeName string) ResolverFunc {
return func(_ context.Context, s *storage.Store, args map[string]interface{}) (interface{}, error) {
if id, ok := args["id"]; ok {
return s.GetByType(typeName, fmt.Sprint(id))
}
return nil, fmt.Errorf("%s requires id", typeName)
}
}
func resolveList(typeName string) ResolverFunc {
return func(_ context.Context, s *storage.Store, args map[string]interface{}) (interface{}, error) {
return s.ListByType(typeName, pl(args))
}
}
func resolveStats(_ context.Context, s *storage.Store, _ map[string]interface{}) (interface{}, error) {
return s.GetByType("LiquidProtocolStats", "1")
}
func pl(args map[string]interface{}) int {
limit := 100
if l, ok := args["first"]; ok {
fmt.Sscanf(fmt.Sprint(l), "%d", &limit)
}
return limit
}