-
Notifications
You must be signed in to change notification settings - Fork 79
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Close #3451 Signed-off-by: Ekaterina Pavlova <[email protected]>
- Loading branch information
1 parent
b8a65d3
commit 8cf1b90
Showing
13 changed files
with
616 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package nep24_test | ||
|
||
import ( | ||
"context" | ||
"math/big" | ||
|
||
"github.com/nspcc-dev/neo-go/pkg/rpcclient" | ||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" | ||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep24" | ||
"github.com/nspcc-dev/neo-go/pkg/util" | ||
) | ||
|
||
func ExampleRoyaltyReader() { | ||
// No error checking done at all, intentionally. | ||
c, _ := rpcclient.New(context.Background(), "url", rpcclient.Options{}) | ||
|
||
// Safe methods are reachable with just an invoker, no need for an account there. | ||
inv := invoker.New(c, nil) | ||
|
||
// NEP-24 contract hash. | ||
nep24Hash := util.Uint160{9, 8, 7} | ||
|
||
// And a reader interface. | ||
n24 := nep24.RoyaltyReader{Invoker: inv, Hash: nep24Hash} | ||
|
||
// Get the royalty information for a token. | ||
tokenID := []byte("someTokenID") | ||
royaltyToken := util.Uint160{1, 2, 3} | ||
salePrice := big.NewInt(1000) | ||
royaltyInfo, _ := n24.RoyaltyInfo(tokenID, royaltyToken, salePrice) | ||
_ = royaltyInfo | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
/* | ||
Package nep24 contains RPC wrappers to work with NEP-24 contracts. | ||
Safe methods are encapsulated into RoyaltyReader structure. | ||
*/ | ||
package nep24 | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"math/big" | ||
|
||
"github.com/nspcc-dev/neo-go/pkg/neorpc/result" | ||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/neptoken" | ||
"github.com/nspcc-dev/neo-go/pkg/util" | ||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem" | ||
) | ||
|
||
// RoyaltyRecipient contains information about the recipient and the royalty amount. | ||
type RoyaltyRecipient struct { | ||
Address util.Uint160 | ||
Amount *big.Int | ||
} | ||
|
||
// RoyaltiesTransferredEvent represents a RoyaltiesTransferred event as defined in | ||
// the NEP-24 standard. | ||
type RoyaltiesTransferredEvent struct { | ||
RoyaltyToken util.Uint160 | ||
RoyaltyRecipient util.Uint160 | ||
Buyer util.Uint160 | ||
TokenID []byte | ||
Amount *big.Int | ||
} | ||
|
||
// RoyaltyReader represents safe (read-only) methods of NEP-24 token. It can be | ||
// used to query data about royalties. | ||
type RoyaltyReader struct { | ||
Invoker neptoken.Invoker | ||
Hash util.Uint160 | ||
} | ||
|
||
// RoyaltyInfo returns the royalty information for the given tokenID, royaltyToken, | ||
// and salePrice. | ||
func (c *RoyaltyReader) RoyaltyInfo(tokenID []byte, royaltyToken util.Uint160, salePrice *big.Int) ([]RoyaltyRecipient, error) { | ||
res, err := c.Invoker.Call(c.Hash, "royaltyInfo", tokenID, royaltyToken, salePrice) | ||
if err != nil { | ||
return nil, err | ||
} | ||
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) | ||
} | ||
|
||
return royalties, nil | ||
} | ||
|
||
// FromStackItem converts a stack item into a RoyaltyRecipient struct. | ||
func (r *RoyaltyRecipient) FromStackItem(item []stackitem.Item) error { | ||
if len(item) != 2 { | ||
return fmt.Errorf("invalid royalty structure: expected 2 items, got %d", len(item)) | ||
} | ||
|
||
recipientBytes, err := item[0].TryBytes() | ||
if err != nil { | ||
return fmt.Errorf("failed to decode recipient address: %w", err) | ||
} | ||
|
||
recipient, err := util.Uint160DecodeBytesBE(recipientBytes) | ||
if err != nil { | ||
return fmt.Errorf("invalid recipient address: %w", err) | ||
} | ||
|
||
amountBigInt, err := item[1].TryInteger() | ||
if err != nil { | ||
return fmt.Errorf("failed to decode royalty amount: %w", err) | ||
} | ||
amount := big.NewInt(0).Set(amountBigInt) | ||
r.Amount = amount | ||
r.Address = recipient | ||
return nil | ||
} | ||
|
||
// RoyaltiesTransferredEventsFromApplicationLog retrieves all emitted | ||
// RoyaltiesTransferredEvents from the provided [result.ApplicationLog]. | ||
func RoyaltiesTransferredEventsFromApplicationLog(log *result.ApplicationLog) ([]*RoyaltiesTransferredEvent, error) { | ||
if log == nil { | ||
return nil, errors.New("nil application log") | ||
} | ||
var res []*RoyaltiesTransferredEvent | ||
for i, ex := range log.Executions { | ||
for j, e := range ex.Events { | ||
if e.Name != "RoyaltiesTransferred" { | ||
continue | ||
} | ||
event := new(RoyaltiesTransferredEvent) | ||
err := event.FromStackItem(e.Item) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to decode event from stackitem (event #%d, execution #%d): %w", j, i, err) | ||
} | ||
res = append(res, event) | ||
} | ||
} | ||
return res, nil | ||
} | ||
|
||
// FromStackItem converts a stack item into a RoyaltiesTransferredEvent struct. | ||
func (e *RoyaltiesTransferredEvent) FromStackItem(item *stackitem.Array) error { | ||
if item == nil { | ||
return errors.New("nil item") | ||
} | ||
arr, ok := item.Value().([]stackitem.Item) | ||
if !ok || len(arr) != 5 { | ||
return errors.New("invalid event structure: expected array of 5 items") | ||
} | ||
|
||
b, err := arr[0].TryBytes() | ||
if err != nil { | ||
return fmt.Errorf("failed to decode RoyaltyToken: %w", err) | ||
} | ||
e.RoyaltyToken, err = util.Uint160DecodeBytesBE(b) | ||
if err != nil { | ||
return fmt.Errorf("invalid RoyaltyToken: %w", err) | ||
} | ||
|
||
b, err = arr[1].TryBytes() | ||
if err != nil { | ||
return fmt.Errorf("failed to decode RoyaltyRecipient: %w", err) | ||
} | ||
e.RoyaltyRecipient, err = util.Uint160DecodeBytesBE(b) | ||
if err != nil { | ||
return fmt.Errorf("invalid RoyaltyRecipient: %w", err) | ||
} | ||
|
||
b, err = arr[2].TryBytes() | ||
if err != nil { | ||
return fmt.Errorf("failed to decode Buyer: %w", err) | ||
} | ||
e.Buyer, err = util.Uint160DecodeBytesBE(b) | ||
if err != nil { | ||
return fmt.Errorf("invalid Buyer: %w", err) | ||
} | ||
|
||
e.TokenID, err = arr[3].TryBytes() | ||
if err != nil { | ||
return fmt.Errorf("failed to decode TokenID: %w", err) | ||
} | ||
|
||
e.Amount, err = arr[4].TryInteger() | ||
if err != nil { | ||
return fmt.Errorf("failed to decode Amount: %w", err) | ||
} | ||
|
||
return nil | ||
} |
Oops, something went wrong.