gnolang / gno

Gno: An interpreted, stack-based Go virtual machine to build succinct and composable apps + Gno.land: a blockchain for timeless code and fair open-source
https://gno.land/
Other
842 stars 343 forks source link

[GnoVM] OrigSend for realms called from another realm #1887

Open r3v4s opened 3 months ago

r3v4s commented 3 months ago

FYI, std.GetOrigSend() will return very first coin that being sent in current stack frame.

For example, 1) User -> ContractA // send 1234ugnot 2) (Automatically) ContractA -> ContractB // send 999ugnot

In above case, '1234ugnot' is origSend but what about '999ugnot'? As view of ContractB, is there ways to parse 999ugnot (which isn't orig send) ??

i.e looking for something like (GetOrigCaller vs PrevRelm.Addr) (GetOrigSend vs XXXX)

txtar testing

loadpkg gno.land/p/demo/ufmt

gnoland start

gnokey maketx addpkg -pkgdir $WORK/second -pkgpath gno.land/r/second -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1

gnokey maketx addpkg -pkgdir $WORK/first -pkgpath gno.land/r/first -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1

gnokey maketx call -pkgpath gno.land/r/first -func Receive -send 1234ugnot -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1

-- first/realm.gno --
package first

import (
    "std"
    "gno.land/p/demo/ufmt"

    "gno.land/r/second"
)

func Receive() (string, int64) {
    // receive ugnot from user and send to another realm
    return SendTo()
}

func SendTo() (string, int64) {
    banker := std.GetBanker(std.BankerTypeRealmSend)
    send := std.Coins{{"ugnot", int64(999)}}
    banker.SendCoins(std.CurrentRealm().Addr(), std.DerivePkgAddr("gno.land/r/second"), send)

    return second.Receive()
}

-- second/realm.gno --
package second

import (
    "std"
)

func Receive() (string, int64) {
    coins := std.GetOrigSend() // origSend will be '1234ugnot' 
    // TODO: ways to parse 999ugnot ??
    coin := coins[0]

    return coin.Denom, coin.Amount
}
leohhhn commented 3 months ago

Hey, thank you for bringing this up - as far as I know, we do not have a way to do this right now, which is very bizarre considering it is a much needed feature most apps, let alone a DeFi app.

For anyone reading - to draw an analogy: Getting the origin caller, and the previous caller is not the same - this is why we have GetOrigCaller & PrevRealm. The same needs to exist, but for coins.

I will add this to the next engineering call to be discussed. Thank you @r3v4s again for finding this issue.

EDIT: thinking about this a little more, there should only be one type of parsing coins - parsing coins from the immediate, previous caller (similar to what msg.value is in EVM). In @r3v4s's case, this would mean that the first realm only has access to coins sent in by the user, and the second realm only has access to coins sent in by the first realm.

leohhhn commented 1 week ago

@thehowl This is an issue that is quite important. Does it make sense to include it into your std refactor?

After discussing internally, we definitely need a way to fetch PrevRealm Coins, as well as possibly remove OrigSend coins. @moul, how should we go about implementing this?

I see two possible options - either we create a std.PrevRealmCoins() function, or possibly add a Coins field into the Realm struct and make it accessible via getters - not sure how feasible the latter is.

We also need to keep in mind the way we send coins from one realm to another. Right now, this is banker.SendCoins(std.CurrentRealm().Addr(), otherRealmAddr, amt).

cc @Kouteki

Kouteki commented 1 week ago

The std refactor is part of the Test4 post launch milestone, same as this issue. We'll be tackling it after the launch.

thehowl commented 1 week ago

@leohhhn thanks for bringing it up, it's actually something I remembered in the back of my head and that I introduced it in the design, though still TODO for now.

The idea is that you have the Deposits() function instead of OrigSend, with the idea of being usable also within called realms. I'm still unsure on how to make it work further down in the call stack (ie. should it contain everything that was sent to the realm in the same tx using banker? Or should there be a way to call a function with a deposit?). But I'm trying to keep it in mind as I proceed.