NEAR Live Contract Review | Episode 16: Meta Staking Pool
Nothing is loaded from YouTube until you press play, and then without tracking cookies.
About this video
Meta Staking Pool is a project by Narwallets. It introduces a new staking pool that allows to diversify the stake into multiple other pools and mint a new token, which provides immediate liquidity for the delegators. The liquidity providers can earn profit for providing a liquidity for both staked and unstaked tokens and allowing execute instant swaps between them.
Overall the contract is fairly complex and we didn't have time to review it completely. We've mostly looked in the main `lib.rs` file and found a few issues around reusing accounts for multiple roles that can be addressed.
What's Meta Pool: narwallets.github.io/meta-pool Source code: github.com/Narwallets/meta-pool
Episode #15: • NEAR Live Contract Review | Episode 15: Be...
Follow the latest from NEAR Protocol on,
Website: near.org Discord: near.chat Blog: near.org/blog Twitter: near.ai/twitter GitHub: near.ai/github
#Blockchain #FutureIsNEAR #NEAR #NEARProtocol
Transcript
4,774 wordsCaptions as YouTube publishes them, usually machine-generated, so product and people names can be misheard.
- 0:00
okay hey everyone uh this is eugene and today we have this patrick matt and william and we gonna record another episode of live contract reviews and today we actually have a request from metapol so there's a new contract and it creates a new staking token so let's actually dive in i i started compiling it so i compiled it away and i'm doing uh testing right now to see if it actually works
- 0:48
and metapool is a project by narwhal wallet which is a meta mask like wallet and the idea here let's see what you can do you can deposit withdraw buy okay something didn't work in tests which maybe maybe it was um a wrong build or something maybe my um my version of this is incorrect so anyway just test that's my capture okay uh anyway it does compile which is good so tests are separate uh so you can buy as near stake then you can sell
- 1:54
which gives you immediate on staking so it's kind of wrapped stake in token so you can do unstake weight finish i guess at stake and stick weight finish withdraw that allows you probably to unstake so you can also stake which gives you a token uh you can provide liquidity to remove liquidity or retrieve unstaked and distributes taking us taking okay all right so majority of work it looks like it's done so let's jump into the source of
- 2:40
leap and see what it is so it has some consons you can get contact info that's cool um oh by the way it uses a near sdk tag 240 which is fairly old and even so it's called four zero two four zero it's still probably older than this one maybe that was just a patch so it may be not as old but somewhat in the middle soon we're gonna publish 3.0 that has a bunch of cool stuff and a lot of helpers so that would be nice
- 3:34
okay so it's split into multiple modules there's a allocator internal account i don't know what it is debug but that's actually a method that we introduced uh it's a log macro so this is external api to stake and pull and it implements a stake input this is kind of interesting what is this okay just uh to make it clear that this is a string so staking pool is a core contract that
- 4:24
is defined in core contracts and the api is unknown and this is a standard apis there's also a separate api that you probably want to use which is a get account that returns you a composed value of three of this essentially now meta's taking pool owner callback so this is us i assume so this is this contract and it implements this bunch of callbacks index amount stake maybe deposit and stake and stay
- 5:17
calm on get result from transfer poll polar result i actually not sure what it is for but it looks like it's some legacy logic around when the transfers are enabled total balance so this is our core contract and it does implement default for some reason now it's a pull info it's not core contract uh okay so it has some data let's scroll down to the main contract so main country called metapol it has owner id
- 6:14
mean account balance some metadata total available not clear in what balance is total available is it near a token total for staking total actually staked total stake shares okay total meta okay so that was near and this is more data said so this is what i started to see like more complicated contracts they get way more fields in the main structure which means every time you do any operation on the contract you have to deserialize all of this and
- 7:20
later research serialize all of this back so this is cost you gas and storage costs for writing it and reading it and for fungible token metadata we introduced a new collection that calls a lazy option and this is what i talked in the last um review about the bari cards where we have trade data that doesn't need to be deserialized every time unless you actually want to trade so it might be helpful to split
- 8:01
our main structure into this lazy option fields and implement them separately so they don't need to be deserialized unless you actually operate on it and yeah we can talk about lazy option later uh in the next video once we actually publish it so there are some fields so there's an ordered map on accounts um the string here i would prefer to use account id and the reason for this is it makes it a little clearer that this is an account and not an
- 8:44
actual could you also use a valid account id type yeah you can use a valid account id as well if it supports borsch so let's see if it does yeah it does support burst so it should be fine also i started to look into how to decrease thrust size and there is huge amount of logic coming from strings because let's say if you go into string then you have a method such as from ugf8 so from utf-8 method takes a vector and tries to parse this vector so we actually spend extra data
- 9:38
every time we parse an account id as a valid utf8 in theory account cannot even have an escape character inside so it will be immediately non-valid so what we may want to do is to to have an account id represented as a vector of u8 internally and not as a string but then you can implement a display for the account id and conversion to string if you want and stuff like this but that will give us some performance boost every time we deserialize a string we don't need to call from utf-8 on the
- 10:18
string staking pulls as a vector so you cannot really have that many staking pools which is probably what we want it's a list of pools you want to diversify in so it's not in an order it's not a persistent vector which means we will deserialize all this data every time which is also another inconvenience it's kind of fixed size but it has an account id as well now loan requests not sure what it is but it's a lookup map
- 11:01
and then a bunch of extra configuration data operator is probably the one who rebalances it's maybe an account id as well and treasure is an account id as well cool so there's a lot of data um in the main structure which will cost extra gas for deserializing it but the data is not that large so it's a bunch of integers which uh in binary serialization this realization are fairly cheap so it shouldn't be a big issue okay
- 11:45
um so we're going into in here contract implements several trades deposit trades they can pull meta staking and multi-talking cool we actually don't have a standard for multi-token yet we only like did one contract token for fungible token but i guess some contracts may need to support multiple tokens so they will have to be separate okay um all right so initializing it uses some
- 12:35
nice mechanics where you say how much gas you use in in a core or in a common to the method ideally we wouldn't need to do this if there was um online simulation like on rpc level where you can just like yeah tell me how much this transaction gonna take and it will tell you so it attaches somewhat the same amount of gas needed um so we initialize a bunch of stuff and um and we use return self but probably a rusty way to just do
- 13:32
this pink and it should pink so deposit it that's internal deposit in this draw so one thing we started doing for fungible token standard is um security consideration for everything that is payable so in this case if this draw method is payable like it operates on the amount of like positive balances so in theory we want to enforce that this is like one yokta um maybe we should even like introduce a macro right now something like not payable but like
- 14:20
require one yakta on on the api that will actually check that the attached deposit is one yocta and eventually we can turn it into contract metadata that will use this decorator to say that this method should not be part of the access key and the reason for this is like well maybe you don't want to withdraw it from access key to your balance select to some other place even so withdrawal is fairly safe usually
- 15:09
okay internal withdrawal please draw all so that would be nice to know how much gas this methods cost because constructor you call it only one so you don't really care you can attach like ton of gas for the constructor but for regular methods it would be interesting to know how much gas it takes yeah i i was imagining also some sort of uh gas abstraction where you know in this case when there's uh you know some external contract
- 15:43
that external contract could also tell you how much gas it costs but so so some kind of decorator you're saying or or just like some other like you know like a trait on that contract that would be like they would outline the costs for each method um yeah so but that's that's some fanciness that i just thought about but never hey i see we're looking at something called metapool yeah metapool is a like another version of a steak token but it works with multiple
- 16:24
uh staking pools and that try to bundle them into one contract and uh one token that like kind of diversifies across multiple pools so let's say one pool is like became an active you don't really like lose that much value for the next epoch we read this so this is from narwhal on our wallet from robert no not from robert from lucia lucha and how different is it from steak i don't know yet so steak is one to one so it issues
- 17:11
a token for one staking pool so this is uh one too many so it is just one token but it diversifies across pools that are controlled by operator so it allows you to for example bootstrap low like pools that have lower fee but they also like lower on the list so you don't risk um if one of them falls off but you also like supporting decentralization and do i have to pick all my own pools no i think it will be provided by operator so we don't know yet i'm still
- 17:53
kind of starting to read the api like seems like maybe something where today operator is an individual but operator could be turned into a dow in the future yeah yeah that's how i actually think about access control so like the contract doesn't have to build into the access control itself so every contract doesn't need to like have a separate control per owner and just say one owner to control it and then this owner is going to be a contract itself
- 18:27
that does the governance okay so this is a bunch of stuff like steak and steak let's take all um getting balances getting values all of this is not as interesting you get done already reward fee century uses reward fee i guess similar to um to the other contract let's take a full contract gets taken key no sticking okay it's taken past
- 19:32
get account so it looks like it tries to mimic the staking pool api so it can be considered a stake in pool itself so it's kind of like a metaphor i guess i understand now is it allows you to kind of deposit here and then it start to spread this information well like this data this uh this funds into sticking pools be it accounts get accounting for results into this it's a large one
- 20:26
that's kind of like infrared account okay cool um finish unstaking user method complete sense take action by moving from retrieved from pools to available try finish on steak and bust himself on itself but okay uh journal update account log so it seems like here we can actually use this debug micro from top bias near stake what
- 21:32
okay so it's identical to stake but it's not payable so i guess it has to be playable oh you first deposit and then stick right um sell discount basis so there is some mass that probably you need to check separately why are we doing it in year 16 discount basis pointless why even cup it into a percentage or something like this why not give it like this larger
- 22:38
denominator um all right so this is one that this seems to be interesting let's look at the method to sell as near so this is the amount of stake near you want to sell and this is how much near you want to get at least so you get how many pounds we convert amount to cell if it's less than what is near near is one year okay if less than one year we sell in everything if modern um then we sell the amount like at least
- 23:41
one year then we sell the amount i don't know what is nslp i can't i guess it's some kind of account to keep track of uh um liquidity in your stake liquidity pool or something near to receive so we look at the liquidity uh we call how much you will get if you try to sell to sell so this is where we found uh overflow in vlad's code in the past so let's see if there's some potential to see an overflow here discount basis points is probably
- 24:44
like a liquidity fee that you take apply percent of discount to to the amount to sell and that is using you okay so it uses denominator okay okay so so okay just so this is how discount works so discount works based on only community that is available when you're trying to sell something so so you get less if you're trying to sell
- 25:51
all amount because you don't have uh unstaked balances so so this is how it works so let's say you have a union swap that allows you to convert state balance to unstaked balance at the current rate of the stake balance but then someone keeps this and stake balance and staked in the liquidity pool and they earn uh tokens or they earn um commission for people who needs to skip waiting periods so they don't wanna like wait two days so like four epochs for
- 26:33
unstaking to happen so they just go to the pool and do instant swap and for doing this uh you kind of earning your more often staked balancing or like more of the state tokens into the liquidity pool then you provide the understatement wait for epochs and rebalance the liquidity pool so this gives you ability to make profit on unstaked near and that's the whole goal of this thing
- 27:25
so this one returns iu 128 as well so that's this into it doesn't we don't really need this into okay um so this how much near you receive and if you get less than we panic if you're uh available balance is like less than we also panic but that shouldn't happen because we calculated it straight from available so it's unlikely this may happen it's more like a safety check now how many shares you
- 28:04
have to sell what is share their share to amount yes proportional total shares rounded down if you don't have so many then we panic so we're saying the the liquidity pool decreases the amount of near you'll get near the what why do we need this kind of check so so this check actually verifies that you received less than the amount you sold
- 29:11
but you sold a stineer so in theory estimator can never go lower but this is not true because let's say we have a staking pool bike or something like this and one of the like estimator is non-recoverable it doesn't earn fee well it's theories it should be fine anyway so this one um calculates how much fee you get fee in shares again around it down no stake shares from amount again yeah rounded down
- 29:58
okay we pull a bunch of internal accounts what's important here is none of this should be the same otherwise we will fetch the information that this same account but there are going to be three copies of this account so let's say a treasury is the same as operator in this case we will pull two accounts from the state but they will point to two different copies so if you like modify one here modify one there one of
- 30:29
these operations will not be received so treasury gets cut from fee and near operators get cat then developer swap cut basis points non-configurable so it's like hardcoded um treasury cat operator cut and developer cat normal pcat meta from operation what would be the correct way to deal with the fact that treasury account operator account developers account could potentially all be the same you would probably want to like
- 31:28
save this account here and then pull it only after for operator i can see him you kind of independently operate on treasury first and on operators then on developer then you need to like update it again so maybe you like pull this account again or you do the mask first here so you will do something like this and you know it all here and then you'll do let's check treasury then
- 32:24
where do we save this account user nlp yeah say you do this then operator account will do here and then developer account will be here so you do a bunch of operations all independently well at least it works for this contract like it's possible that it cannot work for a general case in this case you just uh may have to go multiple times to
- 33:27
to like oh let's pull this account again so this assert better be living over here because you computed all the values here and then you don't need to do modifications i think some of this is already in the variable so you don't need to recompute them but anyway so we add stake shares and again this mass probably can be optimized into a variable that is already computed
- 34:32
somewhere here maybe there's different values not sure why it has to be in these variables probably not to leak meta to seller or something but we already updated total meta total meta from operation okay uh anyway so we save the user account to save nslp account again user account can be an operator account potentially so you need to be careful what you do here so maybe when you pull user account you verify that this is not an operator but then how does operator
- 35:30
take the money well like can an operator use it if you don't if you want to avoid it maybe you should save it here before you start modifying these treasury accounts so you would like save but then maybe there's some logic that will be broken at the moment but probably okay or you can introduce um arc reference here so when you uh get an account you don't just get it from the state but you also cache it locally into like some kind of static cache for the duration of the
- 36:17
execution anyway so the only the only problem if the account now can be an operator developer or treasury account so that might trigger some issue because of this operation and that operation not sure if you can just move them here so you don't have to deal with this so can you potentially like move this logic here then maybe you will avoid it because you just remove this operation
- 37:02
not sure why developer takes half of the meta hardcoded here instead of a fee so maybe it's a logic um now next method looks like adding liquidity so this should be interesting as well so you say how much you want to add a third min amount what is mid amount five near oh you can only end at least five years do you know why that would be true well i guess you you want to avoid some
- 37:49
rounding errors so like some other stuff so the the staking tools they deal with this issue by having this kind of magical near fund that is like throwing extra yuktop around him this one doesn't really have this no lp meter stay delta staked and lost shares the total pull value is available balance plus from stake shares plus some stake and shares from amount rounded down
- 38:59
so all this rounding down occasionally causes issues i guess it's a denominator all right amount total total shares total amount the total amount is denominator total shares is a multiplier so you multiply amount by shares divide the total total value and that's how you know how many shares you get so rounding down is fine so we decrease the balance we increase the shares we increase the amount to increase the share so we minted new
- 40:05
liquidity save account so that works remote liquidity should work somewhat similarly realize meta okay so let's see what is realized meta so values and share pending meet realize some give account meta cool so it's a little confusing because there is a method there is a stake in shares and and there's like unstaked and state balances not sure if you need both meta and it's taking shares but probably you
- 41:07
might need this i feel like meta is a stake in share or like shares that we deal with okay um how much we need to remove liquidity valued enough shares so that's interesting because they do not round up the amount so like you you you want to round shares up because you you essentially underpin some rounded error because you remove less shares
- 42:14
then the then the amount also i don't know if less than one year than the remove all makes sense i would probably panic saying this so it kind of like removes unexpected amount so you say hey i want to remove half of the balance well a half of near but instead it's removed full 0.75 so it kind of broke the request so it removed more so instead i would say well you actually need to remove exactly this amount because it's less than one year
- 42:57
instead of letting the user like silently modify the about the contract okay stake shares to remove to remove remove remove yeah it looks like liquidity removal is kind of a little more complicated because you you need to remove from multiple pieces i also think there's might be some rounding errors from all this that come out from shares and like taking pool actually dealt with them by throwing this yoktanya extra from the like trillion pool
- 43:55
trillion of your ten years but this one implementation doesn't do this so i'm not quite sure how all of this works but maybe it works from um because we have near to to to actually like on state near to actually take it so yeah we basically compute around the rounding error comes from near to remove all right cool so get near to remove um to your account you get shares and you get a mistake and we remove your liquidity shares
- 44:46
now you get you do reverse from here you remove uh all the stuff that you do okay so now it actually makes sense for me so you have your account that has uh two by leos ignoring the meta token so we have three values you have available and staked and staked and liquidity shares so forward is well also 50 is a matter and then on liquidity pool is a account that accounts for all the
- 45:30
liquidity for swapping it tracks the same values it would probably make sense to somehow wrap them all together if it's possible into maybe a data structure so you would introduce a method something like add or remove that operates on all four or five values and this way you don't need to write this separately so because you kind of have to do this most of the time not sure why liquidity pro when you provide the liquidity you don't need to
- 46:12
stake i guess it states from your liquid near but interior you should be able to provide liquidity from your already state balance harvest meta stake realized meta what is this bending meta uralic meta i don't know i still don't understand matter is better from liquid pool okay cool so so that's it so this is the main um method i haven't actually fully understood the metal logic uh we're not going to look at the reward loads or reverse
- 47:06
internals utils you kind of looked types uh so this is some information that you may get a reward meter compute rewards delta yeah not sure if i fully understand like why we need both of them in the same contract select but mostly mostly understand except for meta
- 48:07
in theory how would you deal this you already have staking shares uh so why not um just operate on these values instead of like okay maybe there's some distributes taking so yeah that's kind of gonna be expensive so if this is 75 and then this is 100 plus on not 100 but base so total 100 plus 20 so 120 for single stake and pull
- 49:14
but where does it call it call it sp okay so this is some i guess you you need to call it multiple times to like iterate on it so it will do this cool and then there are some callbacks on this which are gonna verify to be succeeded or not and same for staking and then query current stake yeah it probably needs extra time to look into this in more details to make sure there's no like api issues or
- 50:16
anything like this and this is kind of obfuscated from the user so either user doesn't have to deal with this and the operator essentially deals with this kind of stuff so operator is the pull owner so let's say this is standalone pool that like spreads this info then then operator just have to like occasionally ping this some gas okay there's some journal this is a conversions internal and stake and sternal stake
- 51:13
internal destroy so this similar to stake and pull to to be able to act like a regular sticking pool what is this gets they can pull required steak that's not public i knew this is not public okay uh multifunctional token maybe i'm not gonna look there owner is a full owner i'm not sure why i need to add access key that's coming from lockup so it's probably just for debugging
- 52:14
yeah there are a lot of stuff um how big is the contract what so it's like half an egg of the country just because it's a lot of the methods that needs to be handled and a lot of jason in bush
- 53:20
cool uh i think he can stop today but it kind of definitely interesting it's just not very clear from uh from readme or the comments what how the meta token is used and like what is the meta token so it would be good to come back and actually figure out how you can use meta token and where you can use it so i'll stop sharing and we can quickly discuss if there's any particular questions
- 54:18
or comments on this contract no okay uh cool thanks for watching see you next time