commit 190d272daa8fe277974f6e32ae5e78b6afd0f59f Author: Denis Kirillov Date: Mon Dec 16 17:29:49 2024 +0300 NFT lecture material Signed-off-by: Denis Kirillov diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..52bc2dd --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +.idea + +nft/market/*.json +nft/market/*.nef + +nft/nep11/*.json +nft/nep11/*.nef + +nft/nep17/*.json +nft/nep17/*.nef diff --git a/commands.sh b/commands.sh new file mode 100644 index 0000000..c01f7f1 --- /dev/null +++ b/commands.sh @@ -0,0 +1,56 @@ +#!/bin/bash + +# d2a4cff31913016155e38e474a2c06d08be276cf +neo-go wallet nep17 balance --token GAS -r http://localhost:30333 -w /path/to/frostfs-aio/wallets/wallet1.json + + +#################### +# Custom NFT nep11 # +#################### +neo-go contract compile -i nep11/contract.go -o nep11/contract.nef -m nep11/contract.manifest.json -c nep11/contract.yml +neo-go contract deploy -i nep11/contract.nef -m nep11/contract.manifest.json -r http://localhost:30333 -w /path/to/frostfs-aio/wallets/wallet1.json [ NhCHDEtGgSph1v6PmjFC1gtzJWNKtNSadk ] # d9ab2450c7cc775f747cde591a65c4735c97831c NNWjkFL2TneXjgbHbNPEooiEpxb9KqDnoF +neo-go contract testinvokefunction -r http://localhost:30333 d9ab2450c7cc775f747cde591a65c4735c97831c symbol | jq -r '.stack[0].value' | base64 -d && echo +neo-go contract testinvokefunction -r http://localhost:30333 d9ab2450c7cc775f747cde591a65c4735c97831c decimals +neo-go contract testinvokefunction -r http://localhost:30333 d9ab2450c7cc775f747cde591a65c4735c97831c totalSupply +neo-go contract testinvokefunction -r http://localhost:30333 d9ab2450c7cc775f747cde591a65c4735c97831c balanceOf NhCHDEtGgSph1v6PmjFC1gtzJWNKtNSadk # NivQRezLwGP8xFPr87DD5XaYGUKK3BMsTa +neo-go contract testinvokefunction -r http://localhost:30333 d9ab2450c7cc775f747cde591a65c4735c97831c tokensOfList NhCHDEtGgSph1v6PmjFC1gtzJWNKtNSadk | jq -r '.stack[].value | .[].value' | base64 -d | xxd -ps -c 32 +neo-go contract testinvokefunction -r http://localhost:30333 d9ab2450c7cc775f747cde591a65c4735c97831c properties bytes:55d14c99e63092ecd8492180de6e672b539e8dc23eebea5708722a4458e04270 | jq -r '.stack[].value | .[] .value.value' | while IFS=$'\n' read -r line; do echo $line |base64 -d && echo ; done +neo-go contract testinvokefunction -r http://localhost:30333 d9ab2450c7cc775f747cde591a65c4735c97831c ownerOf bytes:55d14c99e63092ecd8492180de6e672b539e8dc23eebea5708722a4458e04270 | jq -r '.stack[0].value' | base64 -d | xxd -ps -c 32 +neo-go contract testinvokefunction -r http://localhost:30333 d9ab2450c7cc775f747cde591a65c4735c97831c tokensList | jq -r '.stack[].value | .[] .value' | base64 -d | xxd -ps -c 32 + +neo-go wallet nep17 transfer -r http://localhost:30333 -w /path/to/frostfs-aio/wallets/wallet1.json --from NhCHDEtGgSph1v6PmjFC1gtzJWNKtNSadk --to NNWjkFL2TneXjgbHbNPEooiEpxb9KqDnoF --amount 20 --token GAS nft-name --await +neo-go wallet nep11 transfer -r http://localhost:30333 -w /path/to/frostfs-aio/wallets/wallet1.json --from NhCHDEtGgSph1v6PmjFC1gtzJWNKtNSadk --to NivQRezLwGP8xFPr87DD5XaYGUKK3BMsTa --token NICENAMES --id 79aef731091472c4395b63b32b2c00c919b9d9538dc1c990381cc8c4609fe9f8 --await + + +###################### +# Custom Token nep17 # +###################### +neo-go contract compile -i nep17/contract.go -o nep17/contract.nef -m nep17/contract.manifest.json -c nep17/contract.yml +neo-go contract deploy -i nep17/contract.nef -m nep17/contract.manifest.json -r http://localhost:30333 -w /path/to/frostfs-aio/wallets/wallet1.json [ NhCHDEtGgSph1v6PmjFC1gtzJWNKtNSadk 100 ] # 7724e3048f013163e97015f00850da015154af18 +neo-go contract testinvokefunction -r http://localhost:30333 7724e3048f013163e97015f00850da015154af18 symbol | jq -r '.stack[0].value' | base64 -d && echo +neo-go contract testinvokefunction -r http://localhost:30333 7724e3048f013163e97015f00850da015154af18 decimals +neo-go contract testinvokefunction -r http://localhost:30333 7724e3048f013163e97015f00850da015154af18 totalSupply +neo-go contract testinvokefunction -r http://localhost:30333 7724e3048f013163e97015f00850da015154af18 balanceOf NhCHDEtGgSph1v6PmjFC1gtzJWNKtNSadk # NivQRezLwGP8xFPr87DD5XaYGUKK3BMsTa + +neo-go wallet nep17 transfer -r http://localhost:30333 -w /path/to/frostfs-aio/wallets/wallet1.json --from NhCHDEtGgSph1v6PmjFC1gtzJWNKtNSadk --to NivQRezLwGP8xFPr87DD5XaYGUKK3BMsTa --amount 20 --token MYTKN --await + + +########## +# Market # +########## +#LE ScriptHash to Address NNAwRTMyVYpvJY2bYuM1qruvQaBXuAxyPR for transfer +neo-go contract compile -i market/contract.go -o market/contract.nef -m market/contract.manifest.json -c market/contract.yml +neo-go contract deploy -i market/contract.nef -m market/contract.manifest.json -r http://localhost:30333 -w /path/to/frostfs-aio/wallets/wallet1.json [ NhCHDEtGgSph1v6PmjFC1gtzJWNKtNSadk 7724e3048f013163e97015f00850da015154af18 d9ab2450c7cc775f747cde591a65c4735c97831c ] # 7724e3048f013163e97015f00850da015154af18 +neo-go contract testinvokefunction -r http://localhost:30333 84adbcd44b941e9446a331f12cf2bfecc504c518 list | jq -r '.stack[0].value | .[] .value | .[] .value.value' | while IFS=$'\n' read -r line; do echo $line |base64 -d && echo ; done + +neo-go wallet nep11 transfer -r http://localhost:30333 -w /path/to/frostfs-aio/wallets/wallet1.json --from NhCHDEtGgSph1v6PmjFC1gtzJWNKtNSadk --to NNAwRTMyVYpvJY2bYuM1qruvQaBXuAxyPR --token d9ab2450c7cc775f747cde591a65c4735c97831c --id cec6d08d9f18169e74e4540566994ea81cac2df30a298229f50262459b4cd3fc --await +neo-go wallet nep17 transfer -r http://localhost:30333 -w /path/to/frostfs-aio/wallets/wallet1.json --from NhCHDEtGgSph1v6PmjFC1gtzJWNKtNSadk --to NNAwRTMyVYpvJY2bYuM1qruvQaBXuAxyPR --amount 10 --token 7724e3048f013163e97015f00850da015154af18 bytes:cec6d08d9f18169e74e4540566994ea81cac2df30a298229f50262459b4cd3fc --await + + +############ +# Wrappers # +############ +neo-go contract generate-rpcwrapper -o wrappers/nep11/rpc_wrapper.go -m nep11/contract.manifest.json -c nep11/contract.yml --hash d9ab2450c7cc775f747cde591a65c4735c97831c +neo-go contract generate-rpcwrapper -o wrappers/nep11/rpc_wrapper.go -m nep11/contract.manifest.json -c nep11/contract.yml +neo-go contract generate-rpcwrapper -o wrappers/nep17/rpc_wrapper.go -m nep17/contract.manifest.json -c nep17/contract.yml +neo-go contract generate-rpcwrapper -o wrappers/market/rpc_wrapper.go -m market/contract.manifest.json -c market/contract.yml diff --git a/nft/NFT.pdf b/nft/NFT.pdf new file mode 100644 index 0000000..86c3d89 Binary files /dev/null and b/nft/NFT.pdf differ diff --git a/nft/go.mod b/nft/go.mod new file mode 100644 index 0000000..f574860 --- /dev/null +++ b/nft/go.mod @@ -0,0 +1,48 @@ +module contract + +go 1.22 + +require ( + github.com/nspcc-dev/neo-go v0.107.1 + github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240727093519-1a48f1ce43ec +) + +require ( + github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20221202181307-76fa05c21b12 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/snappy v0.0.1 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/websocket v1.5.3 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/holiman/uint256 v1.3.1 // indirect + github.com/mr-tron/base58 v1.2.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/nspcc-dev/go-ordered-json v0.0.0-20240830112754-291b000d1f3b // indirect + github.com/nspcc-dev/hrw/v2 v2.0.1 // indirect + github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240305074711-35bc78d84dc4 // indirect + github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.12 // indirect + github.com/nspcc-dev/rfc6979 v0.2.3 // indirect + github.com/nspcc-dev/tzhash v1.7.2 // indirect + github.com/pierrec/lz4 v2.6.1+incompatible // indirect + github.com/prometheus/client_golang v1.20.2 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 // indirect + github.com/twmb/murmur3 v1.1.8 // indirect + go.etcd.io/bbolt v1.3.11 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/crypto v0.26.0 // indirect + golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 // indirect + golang.org/x/net v0.28.0 // indirect + golang.org/x/sys v0.24.0 // indirect + golang.org/x/text v0.17.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c // indirect + google.golang.org/grpc v1.62.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/nft/go.sum b/nft/go.sum new file mode 100644 index 0000000..93e0ea4 --- /dev/null +++ b/nft/go.sum @@ -0,0 +1,256 @@ +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8= +github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w= +github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20221202181307-76fa05c21b12 h1:npHgfD4Tl2WJS3AJaMUi5ynGDPUBfkg3U3fCzDyXZ+4= +github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20221202181307-76fa05c21b12/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bits-and-blooms/bitset v1.14.2 h1:YXVoyPndbdvcEVcseEovVfp0qjJp7S+i5+xgp/Nfbdc= +github.com/bits-and-blooms/bitset v1.14.2/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.14.0 h1:DDBdl4HaBtdQsq/wfMwJvZNE80sHidrK3Nfrefatm0E= +github.com/consensys/gnark-crypto v0.14.0/go.mod h1:CU4UijNPsHawiVGNxe9co07FkzCeWHHrb1li/n1XoU0= +github.com/containerd/containerd v1.7.13 h1:wPYKIeGMN8vaggSKuV1X0wZulpMz4CrgEsZdaCyB6Is= +github.com/containerd/containerd v1.7.13/go.mod h1:zT3up6yTRfEUa6+GsITYIJNgSVL9NQ4x4h1RPzk0Wu4= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= +github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= +github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= +github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v24.0.9+incompatible h1:HPGzNmwfLZWdxHqK9/II92pyi1EpYKsAqcl4G0Of9v0= +github.com/docker/docker v24.0.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= +github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/holiman/uint256 v1.3.1 h1:JfTzmih28bittyHM8z360dCjIA9dbPIBlcTI6lmctQs= +github.com/holiman/uint256 v1.3.1/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo= +github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= +github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= +github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/nspcc-dev/dbft v0.3.1 h1:3qoc65CVJMtdL/627JZH+1Jz839LmdsVN52L4mlP5z8= +github.com/nspcc-dev/dbft v0.3.1/go.mod h1:BNvJkPKTE28r+qRaAk2C3VoL2J9qzox3fvEeJbh7EWE= +github.com/nspcc-dev/go-ordered-json v0.0.0-20240830112754-291b000d1f3b h1:DRG4cRqIOmI/nUPggMgR92Jxt63Lxsuz40m5QpdvYXI= +github.com/nspcc-dev/go-ordered-json v0.0.0-20240830112754-291b000d1f3b/go.mod h1:d3cUseu4Asxfo9/QA/w4TtGjM0AbC9ynyab+PfH+Bso= +github.com/nspcc-dev/hrw/v2 v2.0.1 h1:CxYUkBeJvNfMEn2lHhrV6FjY8pZPceSxXUtMVq0BUOU= +github.com/nspcc-dev/hrw/v2 v2.0.1/go.mod h1:iZAs5hT2q47EGq6AZ0FjaUI6ggntOi7vrY4utfzk5VA= +github.com/nspcc-dev/neo-go v0.107.1 h1:Mef1nLhYj96G6nX8uxKh1tIXFk4PbxgRwOAGAtUsuTY= +github.com/nspcc-dev/neo-go v0.107.1/go.mod h1:YdaKw8mfdXrECqKzLPzJYysJnoey48g5EG+NTdEfjn8= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240727093519-1a48f1ce43ec h1:vDrbVXF2+2uP0RlkZmem3QYATcXCu9BzzGGCNsNcK7Q= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240727093519-1a48f1ce43ec/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY= +github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240305074711-35bc78d84dc4 h1:arN0Ypn+jawZpu1BND7TGRn44InAVIqKygndsx0y2no= +github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240305074711-35bc78d84dc4/go.mod h1:7Tm1NKEoUVVIUlkVwFrPh7GG5+Lmta2m7EGr4oVpBd8= +github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.12 h1:mdxtlSU2I4oVZ/7AXTLKyz8uUPbDWikZw4DM8gvrddA= +github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.12/go.mod h1:JdsEM1qgNukrWqgOBDChcYp8oY4XUzidcKaxY4hNJvQ= +github.com/nspcc-dev/rfc6979 v0.2.3 h1:QNVykGZ3XjFwM/88rGfV3oj4rKNBy+nYI6jM7q19hDI= +github.com/nspcc-dev/rfc6979 v0.2.3/go.mod h1:q3sCL1Ed7homjqYK8KmFSzEmm+7Ngyo7PePbZanhaDE= +github.com/nspcc-dev/tzhash v1.7.2 h1:iRXoa9TJqH/DQO7FFcqpq9BdruF9E7/xnFGlIghl5J4= +github.com/nspcc-dev/tzhash v1.7.2/go.mod h1:oHiH0qwmTsZkeVs7pvCS5cVXUaLhXxSFvnmnZ++ijm4= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0-rc6 h1:XDqvyKsJEbRtATzkgItUqBA7QHk58yxX1Ov9HERHNqU= +github.com/opencontainers/image-spec v1.1.0-rc6/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/opencontainers/runc v1.1.12 h1:BOIssBaW1La0/qbNZHXOOa71dZfZEQOzW7dqQf3phss= +github.com/opencontainers/runc v1.1.12/go.mod h1:S+lQwSfncpBha7XTy/5lBwWgm5+y5Ma/O44Ekby9FK8= +github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= +github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg= +github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shirou/gopsutil/v3 v3.23.7 h1:C+fHO8hfIppoJ1WdsVm1RoI0RwXoNdfTK7yWXV0wVj4= +github.com/shirou/gopsutil/v3 v3.23.7/go.mod h1:c4gnmoRC0hQuaLqvxnx1//VXQ0Ms/X9UnJF8pddY5z4= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 h1:xQdMZ1WLrgkkvOZ/LDQxjVxMLdby7osSh4ZEVa5sIjs= +github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= +github.com/testcontainers/testcontainers-go v0.24.1 h1:gJdZuQIVWnMJTo+CmQMEP7/CAagNk/0jbcUPn3OWvD8= +github.com/testcontainers/testcontainers-go v0.24.1/go.mod h1:MGBiAkCm86yXQoCiipmQCqZLVdk1uFqtMqaU1Or0MRk= +github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= +github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= +github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= +github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= +github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg= +github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/urfave/cli/v2 v2.27.4 h1:o1owoI+02Eb+K107p27wEX9Bb8eqIoZCfLXloLUSWJ8= +github.com/urfave/cli/v2 v2.27.4/go.mod h1:m4QzxcD2qpra4z7WhzEGn74WZLViBnMpb1ToCAKdGRQ= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0= +go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA= +golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= +golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= +golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c h1:NUsgEN92SQQqzfA+YtqYNqYmB3DMMYLlIwUZAQFVFbo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= +google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk= +google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= diff --git a/nft/main.go b/nft/main.go new file mode 100644 index 0000000..1dc2933 --- /dev/null +++ b/nft/main.go @@ -0,0 +1,224 @@ +package main + +import ( + "context" + "fmt" + "math/big" + "os" + "os/signal" + "runtime/debug" + "strconv" + "syscall" + + nftmarket "contract/wrappers/market" + nicenamesnft "contract/wrappers/nep11" + awesomeneotoken "contract/wrappers/nep17" + + "github.com/nspcc-dev/neo-go/pkg/encoding/address" + "github.com/nspcc-dev/neo-go/pkg/rpcclient" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/gas" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "github.com/nspcc-dev/neo-go/pkg/wallet" +) + +func main() { + ctx, _ := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) + + rpcCli, err := rpcclient.New(ctx, "http://localhost:30333", rpcclient.Options{}) + die(err) + + w, err := wallet.NewWalletFromFile("/home/denis/github/tcl/neofs-aio/wallets/wallet1.json") + die(err) + acc := w.GetAccount(w.GetChangeAddress()) + err = acc.Decrypt("", w.Scrypt) + die(err) + + act, err := actor.NewSimple(rpcCli, acc) + die(err) + + hashNFT, err := util.Uint160DecodeStringLE("d9ab2450c7cc775f747cde591a65c4735c97831c") + die(err) + contractNFT := nicenamesnft.New(act, hashNFT) + _ = contractNFT + + hashMarket, err := util.Uint160DecodeStringLE("c6a0eb7e12c4a6e1abe293ade33cd62436c46b24") + die(err) + contractMarket := nftmarket.New(act, hashMarket) + _ = contractMarket + + hashToken, err := util.Uint160DecodeStringLE("7724e3048f013163e97015f00850da015154af18") + die(err) + contractToken := awesomeneotoken.New(act, hashToken) + + contractGAS := gas.New(act) + + printBalances(contractGAS, contractToken, acc.ScriptHash()) + printMarketNFT(contractMarket) + buyNFT(act, contractMarket, contractToken, hashMarket, "my-itmo-nft") + printMarketNFT(contractMarket) + printNFTs(contractNFT, acc.ScriptHash()) + printBalances(contractGAS, contractToken, acc.ScriptHash()) + + //printNFTs(contractNFT, acc.ScriptHash()) + //createNFT(act, contractGAS, hashNFT, "my-itmo-nft") + //printNFTs(contractNFT, acc.ScriptHash()) + //transferMyTKN(act, contractMarket, acc.ScriptHash()) + //printMarketNFT(contractMarket) + //sellNFT(act, contractNFT, hashMarket, "for-market") + //byNFT(act, contractMarket, contractToken, hashMarket, "for-market") + //printMarketNFT(contractMarket) + //printNFTs(contractNFT, acc.ScriptHash()) + +} + +func createNFT(act *actor.Actor, contractGAS *nep17.Token, hashNFT util.Uint160, name string) { + _, err := act.WaitSuccess(contractGAS.Transfer(act.Sender(), hashNFT, big.NewInt(10_0000_0000), name)) + die(err) +} + +func printBalances(contractGAS *nep17.Token, contractToken *awesomeneotoken.Contract, owner util.Uint160) { + fmt.Println("Balances:") + + gasBalance, err := contractGAS.BalanceOf(owner) + die(err) + fmt.Println("GAS balance: ", gasBalance.Uint64()) + + mytknBalance, err := contractToken.BalanceOf(owner) + die(err) + fmt.Println("MYTKN balance: ", mytknBalance.Uint64()) + fmt.Println() +} + +func sellNFT(act *actor.Actor, c *nicenamesnft.Contract, to util.Uint160, name string) { + for _, nft := range listNFTs(c, act.Sender()) { + if nft.Name == name { + _, err := act.WaitSuccess(c.Transfer(to, nft.ID, nil)) + die(err) + return + } + } + + fmt.Println("not found nft: ", name) + fmt.Println() +} + +func transferMyTKN(act *actor.Actor, c *nftmarket.Contract, to util.Uint160) { + _, err := act.WaitSuccess(c.TransferTokens(to, big.NewInt(10_0000_0000))) + die(err) +} + +func buyNFT(act *actor.Actor, c *nftmarket.Contract, ct *awesomeneotoken.Contract, marketHash util.Uint160, name string) { + for _, nft := range listMarketNFT(c) { + if nft.Name == name { + _, err := act.WaitSuccess(ct.Transfer(act.Sender(), marketHash, big.NewInt(10_0000_0000), nft.ID)) + die(err) + return + } + } + + fmt.Println("not found market nft: ", name) + fmt.Println() +} + +func printNFTs(c *nicenamesnft.Contract, owner util.Uint160) { + res := listNFTs(c, owner) + fmt.Println("owner nfts:") + fmt.Println(res) + fmt.Println() +} + +func listNFTs(c *nicenamesnft.Contract, owner util.Uint160) []NFTItem { + res, err := c.TokensOfExpanded(owner, 10) + die(err) + + var list []NFTItem + for _, re := range res { + prop, err := c.Properties(re) + die(err) + + list = append(list, parseMap(prop.Value().([]stackitem.MapElement))) + } + + return list +} + +func printMarketNFT(c *nftmarket.Contract) { + res := listMarketNFT(c) + fmt.Println("market nfts:") + fmt.Println(res) + fmt.Println() +} + +func listMarketNFT(c *nftmarket.Contract) []NFTItem { + res, err := c.List() + die(err) + + var list []NFTItem + for _, re := range res { + items := re.Value().([]stackitem.MapElement) + list = append(list, parseMap(items)) + } + + return list +} + +type NFTItem struct { + ID []byte + Owner util.Uint160 + Name string + PrevOwners int + Created int + Bought int +} + +func (n NFTItem) String() string { + return fmt.Sprintf("%x\nowner: %s\nname: %s\nprevOwners: %d\ncreated block: %d\nlast bought block: %d\n", + n.ID, address.Uint160ToString(n.Owner), n.Name, n.PrevOwners, n.Created, n.Bought) +} + +func parseMap(items []stackitem.MapElement) NFTItem { + var res NFTItem + + for _, item := range items { + k, err := item.Key.TryBytes() + die(err) + v, err := item.Value.TryBytes() + die(err) + + kStr := string(k) + + switch kStr { + case "id": + res.ID = v + case "owner": + res.Owner, err = address.StringToUint160(string(v)) + die(err) + case "name": + res.Name = string(v) + case "preOwners": + res.PrevOwners, err = strconv.Atoi(string(v)) + die(err) + case "created": + res.Created, err = strconv.Atoi(string(v)) + die(err) + case "bought": + res.Bought, err = strconv.Atoi(string(v)) + die(err) + } + } + + return res +} + +func die(err error) { + if err == nil { + return + } + + debug.PrintStack() + _, _ = fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) +} diff --git a/nft/market/contract.go b/nft/market/contract.go new file mode 100644 index 0000000..bba0b25 --- /dev/null +++ b/nft/market/contract.go @@ -0,0 +1,138 @@ +package contract + +import ( + "github.com/nspcc-dev/neo-go/pkg/interop" + "github.com/nspcc-dev/neo-go/pkg/interop/contract" + "github.com/nspcc-dev/neo-go/pkg/interop/iterator" + "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + "github.com/nspcc-dev/neo-go/pkg/interop/storage" + "github.com/nspcc-dev/neo-go/pkg/interop/util" +) + +// Prefixes used for contract data storage. +const ( + ownerKey = 'o' + tokenKey = 't' + nftKey = 'n' + + tokensPrefix = "tt" +) + +type NFTItem struct { + ID []byte + Owner interop.Hash160 + Name string + PrevOwners int + Created int + Bought int +} + +func _deploy(data interface{}, isUpdate bool) { + if isUpdate { + return + } + + args := data.(struct { + Admin interop.Hash160 + Token interop.Hash160 + Market interop.Hash160 + }) + + if args.Admin == nil { + panic("invalid admin") + } + + if len(args.Admin) != 20 { + panic("invalid admin hash length") + } + + ctx := storage.GetContext() + storage.Put(ctx, ownerKey, args.Admin) + storage.Put(ctx, tokenKey, args.Token) + storage.Put(ctx, nftKey, args.Market) +} + +func OnNEP11Payment(from interop.Hash160, amount int, token []byte, data any) { + ctx := storage.GetContext() + callingHash := runtime.GetCallingScriptHash() + if !callingHash.Equals(storage.Get(ctx, nftKey).(interop.Hash160)) { + panic("invalid nft") + } + + if amount != 1 { + panic("invalid amount") + } + + key := append([]byte(tokensPrefix), token...) + if data == nil { + data = []byte{} + } + storage.Put(ctx, key, data) +} + +func List() []map[string]string { + ctx := storage.GetContext() + + nft := storage.Get(ctx, nftKey).(interop.Hash160) + + res := []map[string]string{} + iter := storage.Find(ctx, []byte(tokensPrefix), storage.KeysOnly|storage.RemovePrefix) + for iterator.Next(iter) { + token := iterator.Value(iter).([]byte) + prop := contract.Call(nft, "properties", contract.All, token).(map[string]string) + res = append(res, prop) + } + + return res +} + +func OnNEP17Payment(from interop.Hash160, amount int, data any) { + defer func() { + if r := recover(); r != nil { + runtime.Log(r.(string)) + util.Abort() + } + }() + + ctx := storage.GetContext() + + callingHash := runtime.GetCallingScriptHash() + if !callingHash.Equals(storage.Get(ctx, tokenKey).(interop.Hash160)) { + panic("invalid token") + } + + if amount < 10_0000_0000 { + panic("insufficient funds") + } + + token := data.([]byte) + key := append([]byte(tokensPrefix), token...) + + if storage.Get(ctx, key) == nil { + panic("token not found") + } + storage.Delete(ctx, key) + + nft := storage.Get(ctx, nftKey).(interop.Hash160) + contract.Call(nft, "transfer", contract.All, from, token) +} + +func TransferTokens(to interop.Hash160, amount int) { + defer func() { + if r := recover(); r != nil { + runtime.Log(r.(string)) + util.Abort() + } + }() + + ctx := storage.GetContext() + + owner := storage.Get(ctx, ownerKey).(interop.Hash160) + if !runtime.CheckWitness(owner) { + panic("not witnessed") + } + + tokenHash := storage.Get(ctx, tokenKey).(interop.Hash160) + + contract.Call(tokenHash, "transfer", contract.All, runtime.GetExecutingScriptHash(), to, amount, nil) +} diff --git a/nft/market/contract.yml b/nft/market/contract.yml new file mode 100644 index 0000000..8958584 --- /dev/null +++ b/nft/market/contract.yml @@ -0,0 +1,6 @@ +name: "NFT market" +safemethods: [ "list" ] + +permissions: + - methods: [ "properties","transfer" ] +# - contract: diff --git a/nft/nep11/contract.go b/nft/nep11/contract.go new file mode 100644 index 0000000..65624aa --- /dev/null +++ b/nft/nep11/contract.go @@ -0,0 +1,342 @@ +package contract + +import ( + "github.com/nspcc-dev/neo-go/pkg/interop" + "github.com/nspcc-dev/neo-go/pkg/interop/contract" + "github.com/nspcc-dev/neo-go/pkg/interop/iterator" + "github.com/nspcc-dev/neo-go/pkg/interop/native/crypto" + "github.com/nspcc-dev/neo-go/pkg/interop/native/gas" + "github.com/nspcc-dev/neo-go/pkg/interop/native/ledger" + "github.com/nspcc-dev/neo-go/pkg/interop/native/management" + "github.com/nspcc-dev/neo-go/pkg/interop/native/std" + "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + "github.com/nspcc-dev/neo-go/pkg/interop/storage" + "github.com/nspcc-dev/neo-go/pkg/interop/util" +) + +// Prefixes used for contract data storage. +const ( + balancePrefix = "b" + accountPrefix = "a" + tokenPrefix = "t" + + ownerKey = 'o' + totalSupplyKey = 's' +) + +const ( + minNameLen = 3 +) + +type NFTItem struct { + ID []byte + Owner interop.Hash160 + Name string + PrevOwners int + Created int + Bought int +} + +func _deploy(data interface{}, isUpdate bool) { + if isUpdate { + return + } + + args := data.(struct { + Admin interop.Hash160 + }) + + if args.Admin == nil { + panic("invalid admin") + } + + if len(args.Admin) != 20 { + panic("invalid admin hash length") + } + + ctx := storage.GetContext() + storage.Put(ctx, ownerKey, args.Admin) + + name := ownerAddress(args.Admin) + nft := NFTItem{ + ID: crypto.Sha256([]byte(name)), + Owner: args.Admin, + Name: name, + PrevOwners: 0, + Created: ledger.CurrentIndex(), + Bought: ledger.CurrentIndex(), + } + setNFT(ctx, nft.ID, nft) + addToBalance(ctx, nft.Owner, 1) + addToken(ctx, nft.Owner, nft.ID) + + storage.Put(ctx, totalSupplyKey, 1) +} + +// Symbol returns token symbol, it's NICENAMES. +func Symbol() string { + return "NICENAMES" +} + +// Decimals returns token decimals, this NFT is non-divisible, so it's 0. +func Decimals() int { + return 0 +} + +// TotalSupply is a contract method that returns the number of tokens minted. +func TotalSupply() int { + return storage.Get(storage.GetReadOnlyContext(), totalSupplyKey).(int) +} + +// BalanceOf returns the number of tokens owned by the specified address. +func BalanceOf(holder interop.Hash160) int { + if len(holder) != 20 { + panic("bad owner address") + } + ctx := storage.GetReadOnlyContext() + return getBalanceOf(ctx, mkBalanceKey(holder)) +} + +// OwnerOf returns the owner of the specified token. +func OwnerOf(token []byte) interop.Hash160 { + ctx := storage.GetReadOnlyContext() + return getNFT(ctx, token).Owner +} + +// Properties returns properties of the given NFT. +func Properties(token []byte) map[string]string { + ctx := storage.GetReadOnlyContext() + nft := getNFT(ctx, token) + + result := map[string]string{ + "id": string(nft.ID), + "owner": ownerAddress(nft.Owner), + "name": nft.Name, + "prevOwners": std.Itoa10(nft.PrevOwners), + "created": std.Itoa10(nft.Created), + "bought": std.Itoa10(nft.Bought), + } + return result +} + +// Tokens returns an iterator that contains all the tokens minted by the contract. +func Tokens() iterator.Iterator { + ctx := storage.GetReadOnlyContext() + key := []byte(tokenPrefix) + iter := storage.Find(ctx, key, storage.RemovePrefix|storage.KeysOnly) + return iter +} + +func TokensList() []string { + ctx := storage.GetReadOnlyContext() + key := []byte(tokenPrefix) + iter := storage.Find(ctx, key, storage.RemovePrefix|storage.KeysOnly) + keys := []string{} + for iterator.Next(iter) { + k := iterator.Value(iter) + keys = append(keys, k.(string)) + } + return keys +} + +// TokensOf returns an iterator with all tokens held by the specified address. +func TokensOf(holder interop.Hash160) iterator.Iterator { + if len(holder) != 20 { + panic("bad owner address") + } + ctx := storage.GetReadOnlyContext() + key := mkAccountPrefix(holder) + iter := storage.Find(ctx, key, storage.ValuesOnly) + return iter +} + +func TokensOfList(holder interop.Hash160) [][]byte { + if len(holder) != 20 { + panic("bad owner address") + } + ctx := storage.GetReadOnlyContext() + key := mkAccountPrefix(holder) + res := [][]byte{} + iter := storage.Find(ctx, key, storage.ValuesOnly) + for iterator.Next(iter) { + res = append(res, iterator.Value(iter).([]byte)) + } + return res +} + +// Transfer token from its owner to another user, notice that it only has three +// parameters because token owner can be deduced from token ID itself. +func Transfer(to interop.Hash160, token []byte, data any) bool { + if len(to) != 20 { + panic("invalid 'to' address") + } + ctx := storage.GetContext() + nft := getNFT(ctx, token) + from := nft.Owner + + if !runtime.CheckWitness(from) { + return false + } + + if !from.Equals(to) { + nft.Owner = to + nft.Bought = ledger.CurrentIndex() + nft.PrevOwners += 1 + setNFT(ctx, token, nft) + + addToBalance(ctx, from, -1) + removeToken(ctx, from, token) + addToBalance(ctx, to, 1) + addToken(ctx, to, token) + } + + postTransfer(from, to, token, data) + return true +} + +func getNFT(ctx storage.Context, token []byte) NFTItem { + key := mkTokenKey(token) + val := storage.Get(ctx, key) + if val == nil { + panic("no token found") + } + + serializedNFT := val.([]byte) + deserializedNFT := std.Deserialize(serializedNFT) + return deserializedNFT.(NFTItem) +} + +func nftExists(ctx storage.Context, token []byte) bool { + key := mkTokenKey(token) + return storage.Get(ctx, key) != nil +} + +func setNFT(ctx storage.Context, token []byte, item NFTItem) { + key := mkTokenKey(token) + val := std.Serialize(item) + storage.Put(ctx, key, val) +} + +// postTransfer emits Transfer event and calls onNEP11Payment if needed. +func postTransfer(from interop.Hash160, to interop.Hash160, token []byte, data any) { + runtime.Notify("Transfer", from, to, 1, token) + if management.GetContract(to) != nil { + contract.Call(to, "onNEP11Payment", contract.All, from, 1, token, data) + } +} + +// OnNEP17Payment mints tokens if at least 10 GAS is provided. You don't call +// this method directly, instead it's called by GAS contract when you transfer +// GAS from your address to the address of this NFT contract. +func OnNEP17Payment(from interop.Hash160, amount int, data any) { + defer func() { + if r := recover(); r != nil { + runtime.Log(r.(string)) + util.Abort() + } + }() + + callingHash := runtime.GetCallingScriptHash() + if !callingHash.Equals(gas.Hash) { + panic("only GAS is accepted") + } + + name := data.(string) + if len(name) < 3 { + panic("name length at least 3 character") + } + + price := 10_0000_0000 + if len(name) < 10 { + price += 5_0000_0000 + } + if len(name) < 6 { + price += 5_0000_0000 + } + + if amount < price { + panic("insufficient GAS for minting NFT") + } + + ctx := storage.GetContext() + tokenID := crypto.Sha256([]byte(name)) + if nftExists(ctx, tokenID) { + panic("token already exists") + } + + nft := NFTItem{ + ID: tokenID, + Owner: from, + Name: name, + PrevOwners: 0, + Created: ledger.CurrentIndex(), + Bought: ledger.CurrentIndex(), + } + setNFT(ctx, tokenID, nft) + addToBalance(ctx, from, 1) + addToken(ctx, from, tokenID) + + total := storage.Get(ctx, totalSupplyKey).(int) + 1 + storage.Put(ctx, totalSupplyKey, total) + + postTransfer(nil, from, tokenID, nil) +} + +// mkAccountPrefix creates DB key-prefix for the account tokens specified +// by concatenating accountPrefix and account address. +func mkAccountPrefix(holder interop.Hash160) []byte { + res := []byte(accountPrefix) + return append(res, holder...) +} + +// mkBalanceKey creates DB key for the account specified by concatenating balancePrefix +// and account address. +func mkBalanceKey(holder interop.Hash160) []byte { + res := []byte(balancePrefix) + return append(res, holder...) +} + +// mkTokenKey creates DB key for the token specified by concatenating tokenPrefix +// and token ID. +func mkTokenKey(tokenID []byte) []byte { + res := []byte(tokenPrefix) + return append(res, tokenID...) +} + +// getBalanceOf returns the balance of an account using database key. +func getBalanceOf(ctx storage.Context, balanceKey []byte) int { + val := storage.Get(ctx, balanceKey) + if val != nil { + return val.(int) + } + return 0 +} + +// addToBalance adds an amount to the account balance. Amount can be negative. +func addToBalance(ctx storage.Context, holder interop.Hash160, amount int) { + key := mkBalanceKey(holder) + old := getBalanceOf(ctx, key) + old += amount + if old > 0 { + storage.Put(ctx, key, old) + } else { + storage.Delete(ctx, key) + } +} + +// addToken adds a token to the account. +func addToken(ctx storage.Context, holder interop.Hash160, token []byte) { + key := mkAccountPrefix(holder) + storage.Put(ctx, append(key, token...), token) +} + +// removeToken removes the token from the account. +func removeToken(ctx storage.Context, holder interop.Hash160, token []byte) { + key := mkAccountPrefix(holder) + storage.Delete(ctx, append(key, token...)) +} + +func ownerAddress(owner interop.Hash160) string { + b := append([]byte{0x35}, owner...) + return std.Base58CheckEncode(b) +} diff --git a/nft/nep11/contract.yml b/nft/nep11/contract.yml new file mode 100644 index 0000000..053c3db --- /dev/null +++ b/nft/nep11/contract.yml @@ -0,0 +1,16 @@ +name: "NICENAMES NFT" +supportedstandards: ["NEP-11"] +safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf", "tokens", "properties"] +events: + - name: Transfer + parameters: + - name: from + type: Hash160 + - name: to + type: Hash160 + - name: amount + type: Integer + - name: tokenId + type: ByteArray +permissions: + - methods: ["onNEP11Payment"] diff --git a/nft/nep17/contract.go b/nft/nep17/contract.go new file mode 100644 index 0000000..27c54c4 --- /dev/null +++ b/nft/nep17/contract.go @@ -0,0 +1,114 @@ +package contract + +import ( + "github.com/nspcc-dev/neo-go/pkg/interop" + "github.com/nspcc-dev/neo-go/pkg/interop/contract" + "github.com/nspcc-dev/neo-go/pkg/interop/native/management" + "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + "github.com/nspcc-dev/neo-go/pkg/interop/storage" +) + +// Prefixes used for contract data storage. +const ( + ownerKey = 'o' + totalSupplyKey = 's' +) + +const ( + decimals = 8 + multiplier = 100000000 +) + +func _deploy(data interface{}, isUpdate bool) { + if isUpdate { + return + } + + args := data.(struct { + Admin interop.Hash160 + Total int + }) + + if args.Admin == nil { + panic("invalid admin") + } + + if len(args.Admin) != 20 { + panic("invalid admin hash length") + } + + if args.Total <= 0 { + panic("invalid total supply") + } + + ctx := storage.GetContext() + storage.Put(ctx, ownerKey, args.Admin) + + total := args.Total * multiplier + storage.Put(ctx, args.Admin, total) + storage.Put(ctx, totalSupplyKey, total) +} + +// Symbol returns the token symbol +func Symbol() string { + return "MYTKN" +} + +// Decimals returns the token decimals +func Decimals() int { + return decimals +} + +// TotalSupply returns the token total supply value +func TotalSupply() int { + return storage.Get(storage.GetReadOnlyContext(), totalSupplyKey).(int) +} + +// BalanceOf returns the amount of token on the specified address +func BalanceOf(holder interop.Hash160) int { + if len(holder) != 20 { + panic("bad owner address") + } + return storage.Get(storage.GetReadOnlyContext(), holder).(int) +} + +// Transfer token from one user to another +func Transfer(from interop.Hash160, to interop.Hash160, amount int, data any) bool { + ctx := storage.GetContext() + + if len(from) != 20 || len(to) != 20 { + panic("invalid addresses") + } + + if amount < 0 { + panic("invalid amount") + } + + if !runtime.CheckWitness(from) { + return false + } + + amountFrom := getIntFromDB(ctx, from) + if amountFrom < amount { + return false + } + storage.Put(ctx, from, amountFrom-amount) + + amountTo := getIntFromDB(ctx, to) + storage.Put(ctx, to, amountTo+amount) + + runtime.Notify("Transfer", from, to, amount) + if management.GetContract(to) != nil { + contract.Call(to, "onNEP17Payment", contract.All, from, amount, data) + } + return true +} + +func getIntFromDB(ctx storage.Context, key []byte) int { + var res int + val := storage.Get(ctx, key) + if val != nil { + res = val.(int) + } + return res +} diff --git a/nft/nep17/contract.yml b/nft/nep17/contract.yml new file mode 100644 index 0000000..8c7b3d0 --- /dev/null +++ b/nft/nep17/contract.yml @@ -0,0 +1,14 @@ +name: "Awesome NEO Token" +supportedstandards: ["NEP-17"] +safemethods: ["balanceOf", "decimals", "symbol", "totalSupply"] +events: + - name: Transfer + parameters: + - name: from + type: Hash160 + - name: to + type: Hash160 + - name: amount + type: Integer +permissions: + - methods: ["onNEP17Payment"] diff --git a/nft/wrappers/market/rpc_wrapper.go b/nft/wrappers/market/rpc_wrapper.go new file mode 100644 index 0000000..b604ab6 --- /dev/null +++ b/nft/wrappers/market/rpc_wrapper.go @@ -0,0 +1,80 @@ +// Code generated by neo-go contract generate-rpcwrapper --manifest --out [--hash ] [--config ]; DO NOT EDIT. + +// Package nftmarket contains RPC wrappers for NFT market contract. +package nftmarket + +import ( + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/neorpc/result" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "math/big" +) + +// Invoker is used by ContractReader to call various safe methods. +type Invoker interface { + Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) +} + +// Actor is used by Contract to call state-changing methods. +type Actor interface { + Invoker + + MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error) + MakeRun(script []byte) (*transaction.Transaction, error) + MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error) + MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error) + SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) + SendRun(script []byte) (util.Uint256, uint32, error) +} + +// ContractReader implements safe contract methods. +type ContractReader struct { + invoker Invoker + hash util.Uint160 +} + +// Contract implements all contract methods. +type Contract struct { + ContractReader + actor Actor + hash util.Uint160 +} + +// NewReader creates an instance of ContractReader using provided contract hash and the given Invoker. +func NewReader(invoker Invoker, hash util.Uint160) *ContractReader { + return &ContractReader{invoker, hash} +} + +// New creates an instance of Contract using provided contract hash and the given Actor. +func New(actor Actor, hash util.Uint160) *Contract { + return &Contract{ContractReader{actor, hash}, actor, hash} +} + +// List invokes `list` method of contract. +func (c *ContractReader) List() ([]stackitem.Item, error) { + return unwrap.Array(c.invoker.Call(c.hash, "list")) +} + +// TransferTokens creates a transaction invoking `transferTokens` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) TransferTokens(to util.Uint160, amount *big.Int) (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "transferTokens", to, amount) +} + +// TransferTokensTransaction creates a transaction invoking `transferTokens` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) TransferTokensTransaction(to util.Uint160, amount *big.Int) (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "transferTokens", to, amount) +} + +// TransferTokensUnsigned creates a transaction invoking `transferTokens` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) TransferTokensUnsigned(to util.Uint160, amount *big.Int) (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "transferTokens", nil, to, amount) +} diff --git a/nft/wrappers/nep11/rpc_wrapper.go b/nft/wrappers/nep11/rpc_wrapper.go new file mode 100644 index 0000000..23361d9 --- /dev/null +++ b/nft/wrappers/nep11/rpc_wrapper.go @@ -0,0 +1,99 @@ +// Code generated by neo-go contract generate-rpcwrapper --manifest --out [--hash ] [--config ]; DO NOT EDIT. + +// Package nicenamesnft contains RPC wrappers for NICENAMES NFT contract. +package nicenamesnft + +import ( + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11" + "github.com/nspcc-dev/neo-go/pkg/util" +) + +// Invoker is used by ContractReader to call various safe methods. +type Invoker interface { + nep11.Invoker +} + +// Actor is used by Contract to call state-changing methods. +type Actor interface { + Invoker + + nep11.Actor + + MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error) + MakeRun(script []byte) (*transaction.Transaction, error) + MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error) + MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error) + SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) + SendRun(script []byte) (util.Uint256, uint32, error) +} + +// ContractReader implements safe contract methods. +type ContractReader struct { + nep11.NonDivisibleReader + invoker Invoker + hash util.Uint160 +} + +// Contract implements all contract methods. +type Contract struct { + ContractReader + nep11.BaseWriter + actor Actor + hash util.Uint160 +} + +// NewReader creates an instance of ContractReader using provided contract hash and the given Invoker. +func NewReader(invoker Invoker, hash util.Uint160) *ContractReader { + return &ContractReader{*nep11.NewNonDivisibleReader(invoker, hash), invoker, hash} +} + +// New creates an instance of Contract using provided contract hash and the given Actor. +func New(actor Actor, hash util.Uint160) *Contract { + var nep11ndt = nep11.NewNonDivisible(actor, hash) + return &Contract{ContractReader{nep11ndt.NonDivisibleReader, actor, hash}, nep11ndt.BaseWriter, actor, hash} +} + +// TokensList creates a transaction invoking `tokensList` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) TokensList() (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "tokensList") +} + +// TokensListTransaction creates a transaction invoking `tokensList` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) TokensListTransaction() (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "tokensList") +} + +// TokensListUnsigned creates a transaction invoking `tokensList` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) TokensListUnsigned() (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "tokensList", nil) +} + +// TokensOfList creates a transaction invoking `tokensOfList` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) TokensOfList(holder util.Uint160) (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "tokensOfList", holder) +} + +// TokensOfListTransaction creates a transaction invoking `tokensOfList` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) TokensOfListTransaction(holder util.Uint160) (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "tokensOfList", holder) +} + +// TokensOfListUnsigned creates a transaction invoking `tokensOfList` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) TokensOfListUnsigned(holder util.Uint160) (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "tokensOfList", nil, holder) +} diff --git a/nft/wrappers/nep11/wrapper.go b/nft/wrappers/nep11/wrapper.go new file mode 100644 index 0000000..5436fbc --- /dev/null +++ b/nft/wrappers/nep11/wrapper.go @@ -0,0 +1,73 @@ +// Code generated by neo-go contract generate-wrapper --manifest --out --hash [--config ]; DO NOT EDIT. + +// Package nicenamesnft contains wrappers for NICENAMES NFT contract. +package nicenamesnft + +import ( + "github.com/nspcc-dev/neo-go/pkg/interop" + "github.com/nspcc-dev/neo-go/pkg/interop/contract" + "github.com/nspcc-dev/neo-go/pkg/interop/neogointernal" +) + +// Hash contains contract hash in big-endian form. +const Hash = "\x1c\x83\x97\x5c\x73\xc4\x65\x1a\x59\xde\x7c\x74\x5f\x77\xcc\xc7\x50\x24\xab\xd9" + +// BalanceOf invokes `balanceOf` method of contract. +func BalanceOf(holder interop.Hash160) int { + return neogointernal.CallWithToken(Hash, "balanceOf", int(contract.ReadOnly), holder).(int) +} + +// Decimals invokes `decimals` method of contract. +func Decimals() int { + return neogointernal.CallWithToken(Hash, "decimals", int(contract.ReadOnly)).(int) +} + +// OnNEP17Payment invokes `onNEP17Payment` method of contract. +func OnNEP17Payment(from interop.Hash160, amount int, data any) { + neogointernal.CallWithTokenNoRet(Hash, "onNEP17Payment", int(contract.All), from, amount, data) +} + +// OwnerOf invokes `ownerOf` method of contract. +func OwnerOf(token []byte) interop.Hash160 { + return neogointernal.CallWithToken(Hash, "ownerOf", int(contract.ReadOnly), token).(interop.Hash160) +} + +// Properties invokes `properties` method of contract. +func Properties(token []byte) map[string]any { + return neogointernal.CallWithToken(Hash, "properties", int(contract.ReadOnly), token).(map[string]any) +} + +// Symbol invokes `symbol` method of contract. +func Symbol() string { + return neogointernal.CallWithToken(Hash, "symbol", int(contract.ReadOnly)).(string) +} + +// Tokens invokes `tokens` method of contract. +func Tokens() any { + return neogointernal.CallWithToken(Hash, "tokens", int(contract.ReadOnly)).(any) +} + +// TokensList invokes `tokensList` method of contract. +func TokensList() []any { + return neogointernal.CallWithToken(Hash, "tokensList", int(contract.All)).([]any) +} + +// TokensOf invokes `tokensOf` method of contract. +func TokensOf(holder interop.Hash160) any { + return neogointernal.CallWithToken(Hash, "tokensOf", int(contract.ReadOnly), holder).(any) +} + +// TokensOfList invokes `tokensOfList` method of contract. +func TokensOfList(holder interop.Hash160) []any { + return neogointernal.CallWithToken(Hash, "tokensOfList", int(contract.All), holder).([]any) +} + +// TotalSupply invokes `totalSupply` method of contract. +func TotalSupply() int { + return neogointernal.CallWithToken(Hash, "totalSupply", int(contract.ReadOnly)).(int) +} + +// Transfer invokes `transfer` method of contract. +func Transfer(to interop.Hash160, token []byte, data any) bool { + return neogointernal.CallWithToken(Hash, "transfer", int(contract.All), to, token, data).(bool) +} diff --git a/nft/wrappers/nep17/rpc_wrapper.go b/nft/wrappers/nep17/rpc_wrapper.go new file mode 100644 index 0000000..549adae --- /dev/null +++ b/nft/wrappers/nep17/rpc_wrapper.go @@ -0,0 +1,47 @@ +// Code generated by neo-go contract generate-rpcwrapper --manifest --out [--hash ] [--config ]; DO NOT EDIT. + +// Package awesomeneotoken contains RPC wrappers for Awesome NEO Token contract. +package awesomeneotoken + +import ( + "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17" + "github.com/nspcc-dev/neo-go/pkg/util" +) + +// Invoker is used by ContractReader to call various safe methods. +type Invoker interface { + nep17.Invoker +} + +// Actor is used by Contract to call state-changing methods. +type Actor interface { + Invoker + + nep17.Actor +} + +// ContractReader implements safe contract methods. +type ContractReader struct { + nep17.TokenReader + invoker Invoker + hash util.Uint160 +} + +// Contract implements all contract methods. +type Contract struct { + ContractReader + nep17.TokenWriter + actor Actor + hash util.Uint160 +} + +// NewReader creates an instance of ContractReader using provided contract hash and the given Invoker. +func NewReader(invoker Invoker, hash util.Uint160) *ContractReader { + return &ContractReader{*nep17.NewReader(invoker, hash), invoker, hash} +} + +// New creates an instance of Contract using provided contract hash and the given Actor. +func New(actor Actor, hash util.Uint160) *Contract { + var nep17t = nep17.New(actor, hash) + return &Contract{ContractReader{nep17t.TokenReader, actor, hash}, nep17t.TokenWriter, actor, hash} +}