diff --git a/l4/.gitignore b/l4/.gitignore new file mode 100644 index 0000000..c2bd88b --- /dev/null +++ b/l4/.gitignore @@ -0,0 +1,3 @@ +*.nef +config.json + diff --git a/l4/data.json b/l4/data.json new file mode 100644 index 0000000..a8d4f08 --- /dev/null +++ b/l4/data.json @@ -0,0 +1,42 @@ +{ + "store": { + "item": [ + { + "id": 0, + "category": "Headgear", + "title": "Bucket Hat", + "price": 400 + }, + { + "id": 1, + "category": "Headgear", + "title": "Fugu Bell Hat", + "price": 1700 + }, + { + "id": 2, + "category": "Clothing", + "title": "Lumberjack Shirt", + "price": 800 + }, + { + "id": 3, + "category": "Clothing", + "title": "Pink Hoodie", + "price": 3400 + }, + { + "id": 4, + "category": "Shoes", + "title": "Cyan Trainers", + "price": 700 + }, + { + "id": 5, + "category": "Shoes", + "title": "Red Hi-Tops", + "price": 1800 + } + ] + } +} \ No newline at end of file diff --git a/l4/go.mod b/l4/go.mod new file mode 100644 index 0000000..e25bb2b --- /dev/null +++ b/l4/go.mod @@ -0,0 +1,5 @@ +module oracle + +go 1.20 + +require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231027092558-8ed6d97085d3 diff --git a/l4/go.sum b/l4/go.sum new file mode 100644 index 0000000..0e8d391 --- /dev/null +++ b/l4/go.sum @@ -0,0 +1,2 @@ +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231027092558-8ed6d97085d3 h1:ybQcK5pTNAR+wQU3k4cGeOZN6OCiVcQkbgR3Zl6NFPU= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231027092558-8ed6d97085d3/go.mod h1:J/Mk6+nKeKSW4wygkZQFLQ6SkLOSGX5Ga0RuuuktEag= diff --git a/l4/lottery/config.yml b/l4/lottery/config.yml new file mode 100644 index 0000000..f7834a0 --- /dev/null +++ b/l4/lottery/config.yml @@ -0,0 +1,5 @@ +name: "Lottery" +supportedstandards: [] +events: +permissions: + - methods: [] diff --git a/l4/lottery/lottery_contract.go b/l4/lottery/lottery_contract.go new file mode 100644 index 0000000..d975d20 --- /dev/null +++ b/l4/lottery/lottery_contract.go @@ -0,0 +1,13 @@ +package lottery + +import ( + "github.com/nspcc-dev/neo-go/pkg/interop/runtime" +) + +func IsWinner(playerName string) bool { + if playerName == "demo" { + return true + } + random := runtime.GetRandom() + return random < 100 +} diff --git a/l4/player/config.yml b/l4/player/config.yml new file mode 100644 index 0000000..121ed86 --- /dev/null +++ b/l4/player/config.yml @@ -0,0 +1,5 @@ +name: "Player" +supportedstandards: [] +events: +permissions: + - methods: ["request", "isWinner"] diff --git a/l4/player/player_contract.go b/l4/player/player_contract.go new file mode 100644 index 0000000..461b178 --- /dev/null +++ b/l4/player/player_contract.go @@ -0,0 +1,125 @@ +package player + +import ( + "github.com/nspcc-dev/neo-go/pkg/interop/native/oracle" + "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" +) + +type Player struct { + balance int + items []int +} + +const storeURLKey = "storeURL" + +func _deploy(data interface{}, isUpdate bool) { + if isUpdate { + return + } + + // TODO: change url to point to the new repository after we merge PR + ctx := storage.GetContext() + storeURL := "https://git.frostfs.info/vdomnich-yadro/oracle-demo/raw/branch/master/data.json" + storage.Put(ctx, storeURLKey, storeURL) +} + +func NewPlayer(playerName string) { + ctx := storage.GetContext() + + existingPlayer := storage.Get(ctx, playerName) + if existingPlayer != nil { + panic("player already exists") + } + + items := make([]int, 0) + + // NOTE: the commented code below requires `lottery/lottery_contract.go` to be deployed on the chain + // If you would like to uncomment it, please: + // 1. Deploy `lottery/lottery_contract.go` contract to your chain + // 2. Replace hash inisde `address.ToHash160` with the hash of deployed contract + // 3. Add imports to this file: + // "github.com/nspcc-dev/neo-go/pkg/interop/contract" + // "github.com/nspcc-dev/neo-go/pkg/interop/lib/address" + // 4. Now you're ready to uncomment the code and run player contract in integration with lottery. + // + // lotteryHash := address.ToHash160("NVBGxUL69FJEV9nhrZQQhPbhkzzvsJqF8q") + // isWinner := contract.Call(lotteryHash, "isWinner", contract.ReadOnly, playerName).(bool) + // if isWinner { + // items = append(items, 0) + // } + + player := Player{ + balance: 3000, + items: items, + } + storage.Put(ctx, playerName, std.Serialize(player)) +} + +func getPlayer(playerName string) Player { + ctx := storage.GetReadOnlyContext() + data := storage.Get(ctx, playerName) + if data == nil { + panic("player not found") + } + + return std.Deserialize(data.([]byte)).(Player) +} + +func Balance(playerName string) int { + p := getPlayer(playerName) + return p.balance +} + +func Items(playerName string) []int { + p := getPlayer(playerName) + return p.items +} + +func BuyItem(playerName string, itemID int) { + player := getPlayer(playerName) + for i := range player.items { + if itemID == player.items[i] { + panic("item has already been purchased") + } + } + + ctx := storage.GetContext() + storeURL := storage.Get(ctx, storeURLKey).(string) + + filter := []byte("$.store.item[" + std.Itoa10(itemID) + "]") + + oracle.Request(storeURL, filter, "cbBuyItem", playerName, 2*oracle.MinimumResponseGas) +} + +func CbBuyItem(url string, userData any, code int, result []byte) { + callingHash := runtime.GetCallingScriptHash() + if !callingHash.Equals(oracle.Hash) { + panic("not called from the oracle contract") + } + if code != oracle.Success { + panic("request failed for " + url + " with code " + std.Itoa(code, 10)) + } + runtime.Log("result for " + url + " is: " + string(result)) + + // Remove quotes that surround resulting JSON + resultLen := len(result) + data := std.JSONDeserialize(result[1 : resultLen-1]).(map[string]any) + + price := data["price"].(int) + gearID := data["id"].(int) + + playerName := userData.(string) + player := getPlayer(playerName) + + if player.balance < price { + panic("insufficient balance") + } + + player.balance -= price + player.items = append(player.items, gearID) + + ctx := storage.GetContext() + storage.Put(ctx, playerName, std.Serialize(player)) +} diff --git a/l4/slides.pdf b/l4/slides.pdf new file mode 100644 index 0000000..604b685 Binary files /dev/null and b/l4/slides.pdf differ