331 lines
10 KiB
Go
331 lines
10 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"math/big"
|
|
"os"
|
|
"path"
|
|
"testing"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
const (
|
|
// nftOwnerAddr is the owner of NFT-ND HASHY token (../examples/nft-nd/nft.go).
|
|
nftOwnerAddr = "NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB"
|
|
nftOwnerWallet = "../examples/my_wallet.json"
|
|
nftOwnerPass = "qwerty"
|
|
)
|
|
|
|
func TestNEP11Import(t *testing.T) {
|
|
e := newExecutor(t, true)
|
|
|
|
tmpDir := os.TempDir()
|
|
walletPath := path.Join(tmpDir, "walletForImport.json")
|
|
defer os.Remove(walletPath)
|
|
|
|
// deploy NFT NeoNameService contract
|
|
nnsContractHash := deployNNSContract(t, e)
|
|
neoContractHash, err := e.Chain.GetNativeContractScriptHash(nativenames.Neo)
|
|
require.NoError(t, err)
|
|
e.Run(t, "neo-go", "wallet", "init", "--wallet", walletPath)
|
|
|
|
args := []string{
|
|
"neo-go", "wallet", "nep11", "import",
|
|
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
|
"--wallet", walletPath,
|
|
}
|
|
// missing token hash
|
|
e.RunWithError(t, args...)
|
|
|
|
// good
|
|
e.Run(t, append(args, "--token", nnsContractHash.StringLE())...)
|
|
|
|
// already exists
|
|
e.RunWithError(t, append(args, "--token", nnsContractHash.StringLE())...)
|
|
|
|
// not a NEP11 token
|
|
e.RunWithError(t, append(args, "--token", neoContractHash.StringLE())...)
|
|
|
|
t.Run("Info", func(t *testing.T) {
|
|
checkNNSInfo := func(t *testing.T) {
|
|
e.checkNextLine(t, "^Name:\\s*NameService")
|
|
e.checkNextLine(t, "^Symbol:\\s*NNS")
|
|
e.checkNextLine(t, "^Hash:\\s*"+nnsContractHash.StringLE())
|
|
e.checkNextLine(t, "^Decimals:\\s*0")
|
|
e.checkNextLine(t, "^Address:\\s*"+address.Uint160ToString(nnsContractHash))
|
|
e.checkNextLine(t, "^Standard:\\s*"+string(manifest.NEP11StandardName))
|
|
}
|
|
t.Run("WithToken", func(t *testing.T) {
|
|
e.Run(t, "neo-go", "wallet", "nep11", "info",
|
|
"--wallet", walletPath, "--token", nnsContractHash.StringLE())
|
|
checkNNSInfo(t)
|
|
})
|
|
t.Run("NoToken", func(t *testing.T) {
|
|
e.Run(t, "neo-go", "wallet", "nep11", "info",
|
|
"--wallet", walletPath)
|
|
checkNNSInfo(t)
|
|
})
|
|
})
|
|
|
|
t.Run("Remove", func(t *testing.T) {
|
|
e.In.WriteString("y\r")
|
|
e.Run(t, "neo-go", "wallet", "nep11", "remove",
|
|
"--wallet", walletPath, "--token", nnsContractHash.StringLE())
|
|
e.Run(t, "neo-go", "wallet", "nep11", "info",
|
|
"--wallet", walletPath)
|
|
_, err := e.Out.ReadString('\n')
|
|
require.Equal(t, err, io.EOF)
|
|
})
|
|
}
|
|
|
|
func TestNEP11_OwnerOf_BalanceOf_Transfer(t *testing.T) {
|
|
e := newExecutor(t, true)
|
|
|
|
tmpDir, err := ioutil.TempDir(os.TempDir(), "neogo.test.nftwallet*")
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
os.RemoveAll(tmpDir)
|
|
})
|
|
|
|
// copy wallet to temp dir in order not to overwrite the original file
|
|
bytesRead, err := ioutil.ReadFile(nftOwnerWallet)
|
|
require.NoError(t, err)
|
|
wall := path.Join(tmpDir, "my_wallet.json")
|
|
err = ioutil.WriteFile(wall, bytesRead, 0755)
|
|
require.NoError(t, err)
|
|
|
|
// transfer funds to contract owner
|
|
e.In.WriteString("one\r")
|
|
e.Run(t, "neo-go", "wallet", "nep17", "transfer",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
|
"--wallet", validatorWallet,
|
|
"--to", nftOwnerAddr,
|
|
"--token", "GAS",
|
|
"--amount", "10000",
|
|
"--from", validatorAddr)
|
|
e.checkTxPersisted(t)
|
|
|
|
// deploy NFT HASHY contract
|
|
h := deployNFTContract(t, e)
|
|
|
|
mint := func(t *testing.T) []byte {
|
|
// mint 1 HASHY token by transferring 10 GAS to HASHY contract
|
|
e.In.WriteString(nftOwnerPass + "\r")
|
|
e.Run(t, "neo-go", "wallet", "nep17", "transfer",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
|
"--wallet", wall,
|
|
"--to", h.StringLE(),
|
|
"--token", "GAS",
|
|
"--amount", "10",
|
|
"--from", nftOwnerAddr)
|
|
txMint, _ := e.checkTxPersisted(t)
|
|
|
|
// get NFT ID from AER
|
|
aer, err := e.Chain.GetAppExecResults(txMint.Hash(), trigger.Application)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(aer))
|
|
require.Equal(t, 2, len(aer[0].Events))
|
|
hashyMintEvent := aer[0].Events[1]
|
|
require.Equal(t, "Transfer", hashyMintEvent.Name)
|
|
tokenID, err := hashyMintEvent.Item.Value().([]stackitem.Item)[3].TryBytes()
|
|
require.NoError(t, err)
|
|
require.NotNil(t, tokenID)
|
|
return tokenID
|
|
}
|
|
|
|
tokenID := mint(t)
|
|
|
|
// check the balance
|
|
cmdCheckBalance := []string{"neo-go", "wallet", "nep11", "balance",
|
|
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
|
"--wallet", wall,
|
|
"--address", nftOwnerAddr}
|
|
checkBalanceResult := func(t *testing.T, acc string, amount string) {
|
|
e.checkNextLine(t, "^\\s*Account\\s+"+acc)
|
|
e.checkNextLine(t, "^\\s*HASHY:\\s+HASHY NFT \\("+h.StringLE()+"\\)")
|
|
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+amount+"$")
|
|
e.checkEOF(t)
|
|
}
|
|
// balance check: by symbol, token is not imported
|
|
e.RunWithError(t, append(cmdCheckBalance, "--token", "HASHY")...)
|
|
// balance check: by hash, ok
|
|
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
|
|
checkBalanceResult(t, nftOwnerAddr, "1")
|
|
|
|
// import token
|
|
e.Run(t, "neo-go", "wallet", "nep11", "import",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
|
"--wallet", wall,
|
|
"--token", h.StringLE())
|
|
|
|
// balance check: by symbol, ok
|
|
e.Run(t, append(cmdCheckBalance, "--token", "HASHY")...)
|
|
checkBalanceResult(t, nftOwnerAddr, "1")
|
|
|
|
// balance check: all accounts
|
|
e.Run(t, "neo-go", "wallet", "nep11", "balance",
|
|
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
|
"--wallet", wall,
|
|
"--token", h.StringLE())
|
|
checkBalanceResult(t, nftOwnerAddr, "1")
|
|
|
|
// remove token from wallet
|
|
e.In.WriteString("y\r")
|
|
e.Run(t, "neo-go", "wallet", "nep11", "remove",
|
|
"--wallet", wall, "--token", h.StringLE())
|
|
|
|
// ownerOf: missing contract hash
|
|
cmdOwnerOf := []string{"neo-go", "wallet", "nep11", "ownerOf",
|
|
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
|
}
|
|
e.RunWithError(t, cmdOwnerOf...)
|
|
cmdOwnerOf = append(cmdOwnerOf, "--token", h.StringLE())
|
|
|
|
// ownerOf: missing token ID
|
|
e.RunWithError(t, cmdOwnerOf...)
|
|
cmdOwnerOf = append(cmdOwnerOf, "--id", string(tokenID))
|
|
|
|
// ownerOf: good
|
|
e.Run(t, cmdOwnerOf...)
|
|
e.checkNextLine(t, nftOwnerAddr)
|
|
|
|
// tokensOf: missing contract hash
|
|
cmdTokensOf := []string{"neo-go", "wallet", "nep11", "tokensOf",
|
|
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
|
}
|
|
e.RunWithError(t, cmdTokensOf...)
|
|
cmdTokensOf = append(cmdTokensOf, "--token", h.StringLE())
|
|
|
|
// tokensOf: missing owner address
|
|
e.RunWithError(t, cmdTokensOf...)
|
|
cmdTokensOf = append(cmdTokensOf, "--address", nftOwnerAddr)
|
|
|
|
// tokensOf: good
|
|
e.Run(t, cmdTokensOf...)
|
|
e.checkNextLine(t, string(tokenID))
|
|
|
|
// properties: no contract
|
|
cmdProperties := []string{
|
|
"neo-go", "wallet", "nep11", "properties",
|
|
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
|
}
|
|
e.RunWithError(t, cmdProperties...)
|
|
cmdProperties = append(cmdProperties, "--token", h.StringLE())
|
|
|
|
// properties: no token ID
|
|
e.RunWithError(t, cmdProperties...)
|
|
cmdProperties = append(cmdProperties, "--id", string(tokenID))
|
|
|
|
// properties: ok
|
|
e.Run(t, cmdProperties...)
|
|
e.checkNextLine(t, fmt.Sprintf(`{"name":"HASHY %s"}`, string(tokenID)))
|
|
|
|
// tokensOf: good, several tokens
|
|
tokenID1 := mint(t)
|
|
e.Run(t, cmdTokensOf...)
|
|
fst, snd := tokenID, tokenID1
|
|
if bytes.Compare(tokenID, tokenID1) == 1 {
|
|
fst, snd = snd, fst
|
|
}
|
|
|
|
e.checkNextLine(t, string(fst))
|
|
e.checkNextLine(t, string(snd))
|
|
|
|
// tokens: missing contract hash
|
|
cmdTokens := []string{"neo-go", "wallet", "nep11", "tokens",
|
|
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
|
}
|
|
e.RunWithError(t, cmdTokens...)
|
|
cmdTokens = append(cmdTokens, "--token", h.StringLE())
|
|
|
|
// tokens: good, several tokens
|
|
e.Run(t, cmdTokens...)
|
|
e.checkNextLine(t, string(fst))
|
|
e.checkNextLine(t, string(snd))
|
|
|
|
// balance check: several tokens, ok
|
|
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
|
|
checkBalanceResult(t, nftOwnerAddr, "2")
|
|
|
|
cmdTransfer := []string{
|
|
"neo-go", "wallet", "nep11", "transfer",
|
|
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
|
"--wallet", wall,
|
|
"--to", validatorAddr,
|
|
"--from", nftOwnerAddr,
|
|
}
|
|
|
|
// transfer: unimported token with symbol id specified
|
|
e.In.WriteString(nftOwnerPass + "\r")
|
|
e.RunWithError(t, append(cmdTransfer,
|
|
"--token", "HASHY")...)
|
|
cmdTransfer = append(cmdTransfer, "--token", h.StringLE())
|
|
|
|
// transfer: no id specified
|
|
e.In.WriteString(nftOwnerPass + "\r")
|
|
e.RunWithError(t, cmdTransfer...)
|
|
|
|
// transfer: good
|
|
e.In.WriteString(nftOwnerPass + "\r")
|
|
e.Run(t, append(cmdTransfer, "--id", string(tokenID))...)
|
|
e.checkTxPersisted(t)
|
|
|
|
// check balance after transfer
|
|
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
|
|
checkBalanceResult(t, nftOwnerAddr, "1") // tokenID1
|
|
|
|
// transfer: good, to NEP11-Payable contract, with data
|
|
verifyH := deployVerifyContract(t, e)
|
|
cmdTransfer = []string{
|
|
"neo-go", "wallet", "nep11", "transfer",
|
|
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
|
"--wallet", wall,
|
|
"--to", verifyH.StringLE(),
|
|
"--from", nftOwnerAddr,
|
|
"--token", h.StringLE(),
|
|
"--id", string(tokenID1),
|
|
"string:some_data",
|
|
}
|
|
e.In.WriteString(nftOwnerPass + "\r")
|
|
e.Run(t, cmdTransfer...)
|
|
tx, _ := e.checkTxPersisted(t)
|
|
// check OnNEP11Payment event
|
|
aer, err := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 2, len(aer[0].Events))
|
|
nftOwnerHash, err := address.StringToUint160(nftOwnerAddr)
|
|
require.NoError(t, err)
|
|
require.Equal(t, state.NotificationEvent{
|
|
ScriptHash: verifyH,
|
|
Name: "OnNEP11Payment",
|
|
Item: stackitem.NewArray([]stackitem.Item{
|
|
stackitem.NewByteArray(nftOwnerHash.BytesBE()),
|
|
stackitem.NewBigInteger(big.NewInt(1)),
|
|
stackitem.NewByteArray(tokenID1),
|
|
stackitem.NewByteArray([]byte("some_data")),
|
|
}),
|
|
}, aer[0].Events[1])
|
|
|
|
// check balance after transfer
|
|
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
|
|
checkBalanceResult(t, nftOwnerAddr, "0")
|
|
}
|
|
|
|
func deployNFTContract(t *testing.T, e *executor) util.Uint160 {
|
|
return deployContract(t, e, "../examples/nft-nd/nft.go", "../examples/nft-nd/nft.yml", nftOwnerWallet, nftOwnerAddr, nftOwnerPass)
|
|
}
|
|
|
|
func deployNNSContract(t *testing.T, e *executor) util.Uint160 {
|
|
return deployContract(t, e, "../examples/nft-nd-nns/", "../examples/nft-nd-nns/nns.yml", validatorWallet, validatorAddr, "one")
|
|
}
|