-
Notifications
You must be signed in to change notification settings - Fork 79
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
manifest: support NEP-24 #3560
base: master
Are you sure you want to change the base?
manifest: support NEP-24 #3560
Conversation
40c7142
to
3a9fdda
Compare
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #3560 +/- ##
==========================================
- Coverage 83.26% 83.04% -0.22%
==========================================
Files 334 335 +1
Lines 46488 46738 +250
==========================================
+ Hits 38707 38815 +108
- Misses 6205 6339 +134
- Partials 1576 1584 +8 ☔ View full report in Codecov by Sentry. |
3a9fdda
to
317b65d
Compare
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.
And you need to adjust
func comply(m *manifest.Manifest, checkNames bool, st *Standard) error { |
- Extend
standard.Standard
structure withRequired []string
field. This field includes standards required by this standard. - In
comply()
check that standards marked as "Required" are present in the manifest. No compliance check is needed for them, the presence check is enough since compliance is checked by the calling code for all standards.
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.
Also, the test of RPC bindings generator based on the existing NEP contract is missing, we discussed it in DM.
317b65d
to
4fc2373
Compare
4fc2373
to
d642090
Compare
d642090
to
8cf1b90
Compare
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.
Review is not finished, some new code is uploaded.
examples/nft-nd/nft.yml
Outdated
- name: RoyaltiesTransferred | ||
parameters: | ||
- name: royaltyToken | ||
type: Hash160 | ||
- name: royaltyRecipient | ||
type: Hash160 | ||
- name: buyer | ||
type: Hash160 | ||
- name: tokenId | ||
type: ByteArray | ||
- name: amount | ||
type: Integer |
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.
Not for the NEP11 contract itself, read the spec:
Marketplaces that support this standard MUST emit the event, RoyaltiesTransferred for each recipient, after sending a payment.
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 I should remove it from manifest/standard too?
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.
Yes, it's not a part of the standard that belongs to the NEP11 contract itself. But it should be moved to a separate standard, ref. #3560 (comment).
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.
The test is not finished. I need a test with generated RPC bindings.
pkg/rpcclient/nep24/royalty.go
Outdated
var royalties []RoyaltyRecipient | ||
for _, item := range res.Stack { | ||
royalty, ok := item.Value().([]stackitem.Item) | ||
if !ok || len(royalty) != 2 { | ||
return nil, fmt.Errorf("invalid royalty structure: expected array of 2 items, got %d", len(royalty)) | ||
} | ||
var recipient RoyaltyRecipient | ||
err = recipient.FromStackItem(royalty) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to decode royalty detail: %w", err) | ||
} | ||
royalties = append(royalties, recipient) | ||
} |
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.
Waiting for the response.
What is your expected result? To me, the auto-generated code looks good although it does not includes named return structures, but it depends on the original contract. As I said earlier, it's a matter of separate issue anyway. |
b1d16d4
to
f3b7983
Compare
i have:
and:
and:
|
f3b7983
to
f27de7c
Compare
I wrongly passed as a config file to
|
You're not first one to do this, btw. @smallhive and @tatiana-nspcc both know it happens easily. Long-term this will be solved by removing binding configuration file because of NEP-25. |
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.
#3560 (comment) is still missing.
89dd8dc
to
7b982f2
Compare
@AliceInHunterland, ready for review? |
7b982f2
to
20d25d4
Compare
examples/nft-d/nft.go
Outdated
if val == nil { | ||
recipients = []RoyaltyRecipient{ | ||
{ | ||
Address: owner, |
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.
Not fixed.
} | ||
|
||
// RoyaltiesTransfer an event that MUST be emitted by marketplaces that support the `royaltyInfo` method. | ||
var RoyaltiesTransfer = &Standard{ |
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.
RoyaltiesTransfer an event that MUST
Grammar. Ans it's not an event, make it similar to the reference:
neo-go/pkg/smartcontract/manifest/standard/payable.go
Lines 8 to 9 in a4633ce
// Nep11Payable contains NEP-11's onNEP11Payment method definition. | |
var Nep11Payable = &Standard{ |
var RoyaltiesTransfer
Rename it to Nep24Payable
, make a separate constant for this standard. Add it to the list of standards check.
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.
also added Nep24Payable
to binding.Generate as mfst.ABI.Events = dropStdEvents(mfst.ABI.Events, standard.Nep24Payable)
in case of ctr.IsNep24 = true
. Do we need a separate test (new smartcontract in testdata) for it?
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.
Do we need a separate test (new smartcontract in testdata) for it?
Would be nice, and this contract shouldn't be large in fact. A single transferring method is enough for the testing purposes.
Required: []string{manifest.NEP11StandardName}, | ||
} | ||
|
||
// RoyaltiesTransfer an event that MUST be emitted by marketplaces that support the `royaltyInfo` method. |
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 not true. Marketplaces don't support this method, this method may be supported by NEP11 token contract. If you're copy-pasting from the standard, then make it properly, here's what the standard tells about it:
MUST trigger after marketplaces transferring royalties to the royalty recipient if royaltyInfo method is implemented.
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.
The standard also tells:
The royaltyInfo() function MUST NOT ignore the salePrice, royaltyAmount MAY be dynamic due to salePrice and time.
Marketplaces that support this standard MUST emit the event, RoyaltiesTransferred for each recipient, after sending a payment.
Which is literally my comment. So the comment // RoyaltiesTransfer is an event that MUST trigger after marketplaces transferring royalties to the royalty recipient if royaltyInfo method is implemented.
is more correct?
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.
Oh, I should have left a comment to the standard when it was on the review stage. OK, then choose either you'd like.
an event that MUST trigger
Grammar: an event that MUST be triggered
61e54e4
to
3214732
Compare
3214732
to
cbfda6a
Compare
func New(actor Actor) *Contract { | ||
var hash = Hash | ||
var nep11dt = nep11.NewDivisible(actor, hash) | ||
return &Contract{ContractReader{nep11dt.DivisibleReader, nep24.RoyaltyReader{}, actor, hash}, nep11dt.DivisibleWriter, actor, hash} |
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.
nep24.RoyaltyReader{}
It's not correct, you have to properly initialize it. Make it similar to var nep11dt = nep11.NewDivisible(actor, hash)
.
examples/nft-d/nft.go
Outdated
panic("sale price must be positive") | ||
} | ||
|
||
callingHash := runtime.GetExecutingScriptHash() |
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.
callingHash
It's not calling
, it's executing
.
examples/nft-d/nft.go
Outdated
for _, owner := range owners { | ||
recipients = append(recipients, RoyaltyRecipient{ | ||
Address: owner, | ||
Amount: salePrice / 10, |
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.
Amount: salePrice / 10,
Allocate variable once and then reuse it. And I'd suggest salePrice / 10 / len(owners)
as a unit of royalty payment (consider if there are more than 10 owners), because usually it's some tiny part of the token price that is aimed to support authors; and the rest is taken by marketplace.
examples/nft-d/nft.yml
Outdated
supportedstandards: ["NEP-11"] | ||
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf", "properties", "tokens"] | ||
supportedstandards: ["NEP-11","NEP-24"] | ||
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf", "properties", "tokens","royaltyInfo"] |
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.
Indent?
examples/nft-d/nft.go
Outdated
@@ -34,6 +34,8 @@ const ( | |||
tokenOwnerPrefix = "t" | |||
// tokenPrefix contains map from token id to its properties (serialised containerID + objectID). | |||
tokenPrefix = "i" | |||
// royaltyInfoPrefix contains map from token id to its royalty information. | |||
royaltyInfoPrefix = "r" |
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.
For this contract it's not needed because there's a fixed rate of royalty payment for every token.
} | ||
if ctr.IsNep24 { | ||
mfst.ABI.Methods = dropStdMethods(mfst.ABI.Methods, standard.Nep24) | ||
mfst.ABI.Events = dropStdEvents(mfst.ABI.Events, standard.Nep24Payable) |
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.
dropStdEvents
NEP24Payable is represented as a separate standard, can't be done in one case with NEP24.
// dropTypes removes NamedTypes of NEP-24 from the config if they are used only once. | ||
func dropTypes(cfg binding.Config, std *standard.Standard) binding.Config { | ||
var targetTypeName string | ||
if royaltyInfo, ok := cfg.Types[std.ABI.Methods[0].Name]; ok && royaltyInfo.Value != nil { |
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.
If this method is generic, then you can't use any royalty-relating naming and things like std.ABI.Methods[0].Name
inside this method because std
can be anything. If this method is royalties-specific, then the method name must reflect this fact and no std
argument must be present.
count := 0 | ||
for _, typeDef := range cfg.Types { | ||
if typeDef.Value != nil && typeDef.Value.Name == targetTypeName { | ||
count++ |
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.
Just return immediately if there's another occurrence of this type. And you don't even need count
for that, some bool
is sufficient.
} | ||
if count == 1 { | ||
filteredNamedTypes := maps.Clone(cfg.NamedTypes) | ||
if _, exists := cfg.NamedTypes[targetTypeName]; exists { |
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.
s/exists/ok
} | ||
} | ||
if count == 1 { | ||
filteredNamedTypes := maps.Clone(cfg.NamedTypes) |
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.
Too expensive, you don't need it.
examples/nft-nd/nft.yml
Outdated
@@ -15,5 +15,5 @@ events: | |||
type: ByteArray | |||
permissions: | |||
- hash: fffdc93764dbaddd97c48f252a53ea4643faa3fd | |||
methods: ["update", "destroy"] | |||
methods: ["update", "destroy","setRoyaltyInfo"] |
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.
Revert it. This section allows contract to call native Management's destroy
and update
methods, it's not related to the contract itself.
`Required` contains standards that are required for this standard. Signed-off-by: Ekaterina Pavlova <[email protected]>
80f6607
to
b49d9b6
Compare
nfsoToken1ID = "7e244ffd6aa85fb1579d2ed22e9b761ab62e3486" | ||
storageContractHash = "ebc0c16a76c808cd4dde6bcc063f09e45e331ec7" | ||
storageContractHash = "2e0809e43dc0f81dab1b48e45dccbc1148ee2a2e" |
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.
@AnnaShaleva could you advise, please, I think tests are failing because storageContractHash
is incorrect or maybe was deployed wrongly. However TestCreateBasicChain
hadn't any errors and the storage contract wasn't changed.
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.
You need to regenerate basic chain with saveChain = true
, then fetch relevant data from the test logs (including storage contract hash) and update RC server test variables. From what I see, the following test is failing:
--- FAIL: TestRPC/websocket/batch_with_single_request (0.38s)
--- FAIL: TestRPC/websocket/batch_with_single_request/invokefunction (0.01s)
--- FAIL: TestRPC/websocket/batch_with_single_request/invokefunction/positive,_with_storage_changes (0.00s)
server_test.go:1434:
Error Trace: /home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/services/rpcsrv/server_test.go:1434
/home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/services/rpcsrv/server_test.go:2541
Error: elements differ
extra elements in list A:
([]interface {}) (len=2) {
(dboper.Operation) {
State: (string) (len=7) "Changed",
Key: ([]uint8) (len=5) {
00000000 fa ff ff ff 0b |.....|
},
Value: ([]uint8) (len=7) {
00000000 54 b2 d2 a3 51 79 12 |T...Qy.|
}
},
(dboper.Operation) {
State: (string) (len=7) "Changed",
It happens because its results are non-deterministic if you regenerate test chain. To fix it just update expected storage values for this test.
Regarding the second test:
logger.go:146: 2024-11-08T10:29:22.479+0300 INFO Error encountered with rpc request {"code": -108, "method": "traverseiterator", "params": "[f4f4c870-a739-4856-83de-e36795314d16 16406a2a-8065-494a-8892-9b572a9f77f5 1]"}
--- FAIL: TestRPC/http/traverseiterator (0.02s)
--- FAIL: TestRPC/http/traverseiterator/good (0.00s)
server_test.go:3071:
Error Trace: /home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/services/rpcsrv/server_test.go:3071
Error: Not equal:
expected: 99
actual : 2
Test: TestRPC/http/traverseiterator/good
We haven't changed storage contract in any way, and storageContractHash
contains correct hash because it's the only contract with iterateOverValues
method in the whole repo. Try to regenerate chain one more time and check which values are really stored in the contract storage. Also, try to regenerate chain on master and see if the same error is reproduced.
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.
On master the error is reproduced (changed saveChain to true, generated .acc, changed back saveChain to false, changed constants, run tests). TestClient_IteratorSessions, TestClient_Iterator_SessionConfigVariations - failed.
The same as I have within the newly generated one on nep24. That is why I was thinking about the wrong storage contract hash.
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.
On master the error is reproduced
Create a separate bug for this, and let's fix it firstly. I won't say that the problem is with contract hash, it's something iterator-related (although we didn't touch this code for quite a long time).
Close #3451 Signed-off-by: Ekaterina Pavlova <[email protected]>
Signed-off-by: Ekaterina Pavlova <[email protected]>
b49d9b6
to
7893dbd
Compare
Signed-off-by: Ekaterina Pavlova <[email protected]>
7893dbd
to
0a9b4be
Compare
Close #3451
wrapper generates:
but it should be different.
add tests with
smartcontact/testdata/