Open moul opened 1 year ago
Update from https://github.com/gnolang/meetings/issues/5
We need:
Encode
,Decode
(primitive types).To clarify the above, we need first to figure out how the "ABI" works for argument and return values. I suggest we just use a slice of stringified primitive values
. Later after the prototype we can consider compatibility with ETH ABI, or some other byte form; while also considering how to support structures (non-primitive types).
We don't need Encode
or Decode
exposed in Gno. From the perspective of Gno it should be transparent how it is encoded/decoded. Maybe what we need is to use DefineNative as in stdlibs/stdlibs.go -- then in there we can stringify the arguments and queue it up. We would need a utility method that panics upon encountering non-primitive values. And later we can figure out how to improve this to support Solidity ABI or something else.
Talking about the API, I like the idiomatic Go import.
Another idea trying to merge a dynamic approach with type safety, we can do something like this:
type myInterface interface {
Method(arg string) string
}
i, ok := std.GetRealm("gno.land/r/contract/v2").(myInterface)
if !ok {
[....]
}
out := i.Method("test")
Here is a list of the std.Get*
functions and their use
std.Get*() functions | what they do |
---|---|
GetChainID | return ChainID |
GetHeight | return block height |
GetOrigSend | return Coins sent |
GetOrigCaller | return user address (tx.orign) |
GetOrigPkgAddr | return the first calling Realm |
GetCallerAt | given a frame index, return the caller |
GetBanker | return a BankerType |
GetTimestamp | deprecated |
CurrentRealmPath | return realm path (gno.land/r/example) |
std.Get* potential new one | What they do |
---|---|
GetCaller/GetSender | return the previous Realm calling, if not the user |
GetCallersCount (?) | return the numbers of callers, currently we can't know GetCallerAt(x) limit |
GetPkgAddr | return the current PkgAddr |
GetCaller/GetSender
| return the previous Realm calling, if not the user
How about GetLastCaller
or GetPreviousCaller
? Additionally, would a GetRealmAddr
function that returns the calling realm or nil
be useful?
GetCallersCount (?) | return the numbers of callers, currently we can't know GetCallerAt(x) limit
Options include using GetCallersCount
, returning a struct{addr string, index int}
instead of a string
, or returning nil
if overflow occurs.
GetPkgAddr
| return the current PkgAddr
👍, This would be useful for reusable contracts with init()
functions, as well as for the Render()
function when creating links using the [](prefix+path)
syntax.
How about GetLastCaller or GetPreviousCaller?
I understand this point, i'm fine with GetLastCaller
but i prefer GetCaller
for the reason that a "Caller" already express the sens of "Last" or "previous"
Additionally, would a GetRealmAddr function that returns the calling realm or nil be useful?
i don't this so, people will manage with something like
addr := std.GetRealmAddr()
if addr == nil {
// handle this
}
So it's the same as
addr := std.GetCaller()
if addr == std.GetOrigCaller() {
// handle this
}
GetPkgAddr | return the current PkgAddr
We need a better definition of the difference between a Realm and a Package. which is clear for us but could be unclear for a Gno realm dev.
Because there is "no need" that a package hold assets. Package "context" should be the realm context
i don't this so, people will manage with something like...
I was proposing in addition to GetCaller
, not to replace it.
Because there is "no need" that a package hold assets. Package "context" should be the realm context
In certain cases, I believe it would be prudent to determine the originating package, allowing for the white-listing of a trustworthy intermediate package, thus enabling its use by anyone.
Realm is a way more common, but both make sense IMO.
I was proposing in addition to GetCaller, not to replace it.
Yes i understand that, but since it's taking the same amount of LoC, i don't see the usage.
But i could be easy to add if someone express the need of this function :)
[...] allowing for the white-listing of a trustworthy intermediate package,
I love that idea! but what do you think putting this whitelisting inside of gno.mod
?
And do you think we should keep GetCallerAt()
? Do you have an example of when it could be needed ?
For those joining late:
Our current objective is not to introduce type-unsafe features for inter-contract calls. In Version 1, we will continue to support the existing native import system and may potentially implement asynchronous methods for sending async messages via IBC or local IBC connections.
For scenarios involving account attraction, contract-based multisigs, and proxy patterns, we should prioritize exploring new solutions that align with Version 1 constraints before attempting to adapt patterns from other ecosystems.
Edit, disclaimer:
The aim is to provide a summary of the available options for calling contracts, as well as to summarize the investigation of new techniques.
std.{Send,Recv}
std.Call
std.Ctx{Set,Get}
UPDATE: we shouldn't implement .Call or .CtxSet,Get
Current options
Idiomatic go import
The recommended approach involves importing a package or a realm and invoking an exported function, which is highly idiomatic, maintains type-safety, and offers better reliability than a micro-service relying on TCP. Furthermore, this method could enable auto-complete functionality over time.
The proposed solution is limited in that it does not support importing a dynamic package with a variable name.
Source: https://github.com/gnolang/gno/blob/master/examples/gno.land/r/demo/boards/misc.gno
Potential upcoming solutions
IBC-compatible API
The proposed enhancements include a contract-contract interface using two monodirectional channels to support asynchronous and potentially synchronous calls, with a similar API to the upcoming IBC interface. The channels could also enable external transaction triggering based on chain hooks.
At this stage, it is unclear whether the contract-contract interface would be limited to simple types only, require marshalling, or enable specifying the expected type with simple typed channels.
One of the proposed enhancements involves introducing a new
std.Call/Invoke("contract-addr", "method", params...)
method."Pseudo-code:
Related work:
473: implementation of an IBC-style API without need for chan support.
New
std.Call/Invoke
method, for dynamic synchronous callsWarning: user security concerns, losing type-safety
The proposed approach is similar to the IBC one (above), with the exception that it involves a new
std.Call
helper that is necessarily synchronous.Pseudo-code:
Related work:
std.Call
to support dynamic import and call.std.CreatePackage
andstd.CallPackage
.Helpers to extend the calling context
Warning: it should be avoided if we can just update libraries to take a
Context
argument.Providing helpers to taint the calling context with metadata, which would enable contracts to be called as before while extending how the dependent contract views its calling graph.
The proposed method for calling contracts has similar limitations to the existing method, in that it is static. However, it adds the capability to simulate various contract-contract interactions, such as specifying that an intermediary contract should be considered as an account and store assets.
Pseudo-code:
Related work:
683: proposal to enhance Stack/Frames/Context.
335: initial attempt to define a
std.ExecAsPkg
helper.644: enhanced version of #335.