-
Notifications
You must be signed in to change notification settings - Fork 1
WIP: Support DAMM v2 #470
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
WIP: Support DAMM v2 #470
Conversation
'totalTradingQuoteFee', COALESCE(artist_coin_pools.total_trading_quote_fee, 0), | ||
'creatorWalletAddress', COALESCE(artist_coin_pools.creator_wallet_address, '') | ||
) AS dynamic_bonding_curve, | ||
ROW_TO_JSON(calculate_artist_coin_fees(artist_coins.mint)) AS artist_fees, |
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.
note: kinda yucky but because I'm doing ROW_TO_JSON here, the struct deserializes using the JSON struct tag names. Thus the JSON struct tags need to be underscores. To my knowledge, it's not possible to have one JSON struct tag for serializing and another for deserializing.
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.
instead could we just select the fields off of the resulting row from calculate_artist_coin_fees?
i think this is also okay though
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.
yeah and use JSON_BUILD_OBJECT? we could do that. I could also make that function return camelCased columns...
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.
this should live somewhere higher up i think
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.
looking for ideas on how to organize now that there will be likely multiple indexers in this folder. I think the current model isn't great of having everything dumped in this one package. One thing I'm considering is separating out the processors and indexers perhaps... then there could be indexer.ArtistCoins
and indexer.DammV2
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.
it's possible we support a coin that's launched outside of our dbc, so i like having indexer.Dbc and indexer.DammV2
I don't hate what you've done here though tbh, it's not bad to follow!
solana/indexer/damm_v2.go
Outdated
return grpcClients, nil | ||
} | ||
|
||
func watchPgNotification(ctx context.Context, pool database.DbPool, notification string, callback notificationCallback, logger *zap.Logger) error { |
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.
planning to move this to utils or somewhere and apply a similar strategy to the existing indexer.
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.
looking great
* ( | ||
position.unlocked_liquidity + position.vested_liquidity + position.permanent_locked_liquidity | ||
) | ||
/ POWER (2, 128) |
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.
what's this?
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.
So the liquidity is represented as an enormous integer but is actually shifted 128 bits - AI says its a method for doing precise math w/ large fixed fractional components (In this case, 128bits for the whole component, and 128bits for the fractional component?)
pretty interesting.. wish they had better explainers
+ position.fee_b_pending | ||
) AS total_damm_v2_fees, | ||
( | ||
(pool.fee_b_per_liquidity - position.fee_b_per_token_checkpoint) |
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.
what is fee_b_per_token_checkpoint?
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.
This is updated as you claim and marks the amount you've already claimed basically
pool TEXT PRIMARY KEY REFERENCES sol_meteora_damm_v2_pools(address) ON DELETE CASCADE, | ||
protocol_fee_percent SMALLINT NOT NULL, | ||
partner_fee_percent SMALLINT NOT NULL, | ||
referral_fee_percent SMALLINT NOT NULL, |
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.
what's referral fee?
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.
I don't actually know! Would be curious to learn (it comes from the fee configuration on the Pool struct on chain: https://explorer.solana.com/address/9avVRGRvPsSYiXKBMHnC6RNPbwN5yE3v7fD8FibgScwA/anchor-account looks like ours is 20%?)
"total_claimed_a_fee": metrics.TotalClaimedAFee, | ||
"total_claimed_b_fee": metrics.TotalClaimedBFee, | ||
}) | ||
return err |
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.
looks good
statsCtx := context.WithoutCancel(ctx) | ||
statsJob.ScheduleEvery(statsCtx, 5*time.Minute) | ||
go statsJob.Run(statsCtx) | ||
// statsJob := jobs.NewCoinStatsJob(s.config, s.pool) |
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.
comments?
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.
good catch - yeah had these turned off for testing
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.
things look pretty great here to me. my only concern is the amount of code we've written, especially for the instruction.go files - i really think we should use their packages for these if we can. i feel like there should be ways to not have to implement a lot of this ourselves. especially stuff like Uint256LE
SELECT | ||
pool.token_a_mint AS mint, | ||
( | ||
pool.fee_b_per_liquidity |
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.
confirming that b
is always what we care about (I think so) and that a
is our "base" mint
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.
it's possible we support a coin that's launched outside of our dbc, so i like having indexer.Dbc and indexer.DammV2
I don't hate what you've done here though tbh, it's not bad to follow!
subscription.Slots = make(map[string]*pb.SubscribeRequestFilterSlots) | ||
subscription.Slots["checkpoints"] = &pb.SubscribeRequestFilterSlots{} | ||
|
||
// fromSlot := uint64(372380625) |
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.
👻
} | ||
|
||
switch inst.TypeID { | ||
case meteora_dbc.InstructionImplDef.TypeID(meteora_dbc.Instruction_MigrationDammV2): |
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.
nice
'totalTradingQuoteFee', COALESCE(artist_coin_pools.total_trading_quote_fee, 0), | ||
'creatorWalletAddress', COALESCE(artist_coin_pools.creator_wallet_address, '') | ||
) AS dynamic_bonding_curve, | ||
ROW_TO_JSON(calculate_artist_coin_fees(artist_coins.mint)) AS artist_fees, |
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.
instead could we just select the fields off of the resulting row from calculate_artist_coin_fees?
i think this is also okay though
I'm not aware of any of their packages doing the deserializing of instructions. Most of their SDKs are focused on building the instructions... They do have deserializing of state that we could be using, but they use different packages etc and it honestly seems more manual than what I have here. I have copied the structs nearly verbatim but using different types for eg UInt128 that are native to the binary deserializer that we use, and worked to make these fit well with our current patterns rather than adopting a new one. I think the state gives me a lot of confidence actually because it's all just using the borsh decoder directly rather than handrolling, and it's easily tested against. It might be nice actually to have something that converts IDL files into go structs for us, but that's probably unnecessary. One concern I have bringing in the package they have is that we will have mismatching versions of things and increased bloat, especially when all we really need are the types in the account state themselves, and the patterns for things will be different per program. Having one common place and pattern for communicating with programs and chain state makes sense to me. As far as Uint256LE, yeah agree that's a bit of a smell... it should map 1:1 to what they do though, and requires less manual logic to work with... |
Main changes:
Extras:
withRetries
helper that doesn't have a resultUint256LE
helper struct for deserializing 256-bit unsigned integers in little endianProcessSignature
skipping failed transactions on retry because they were in the fetch cacheTODO: