Skip to content

OwnedObject Input #123

Description

@viol3

Describe the feature or improvement you're requesting

Hey. Is this SDK supporting OwnedObjects as an argument? I am getting Unknow Call Arg Type error after I send owned object's id. Here Unity and Move codes :

module lucky_game::lucky_game
{
    use sui::object::{Self, UID};
    use sui::balance::{Self, Balance};
    use sui::tx_context::{Self, TxContext};
    use sui::transfer;
    use sui::coin::{Self, Coin};
    use sui::sui::SUI;
    use sui::random::{Self, Random};
    use std::hash;
    use sui::event;
    use std::vector;

    public struct DiceValue has copy, drop
    {
        value: u8,
    }

    public struct Commit has key
    {
        id: UID,
        player: address,
        commit_hash: vector<u8>,
        amount: u64,
        input: u8,
    }

    public struct CHEST has key
    {
        id: UID,
        chestBalance: Balance<SUI>,
        owner: address,
    }

    const PUBLISHER: address = @0x0d9b5ca4ebae5f4a7bd3f17e4e36cd6f868d8f0c5a7f977f94f836631fe0288d;

    public fun create(ctx: &mut TxContext)
    {
        assert!(tx_context::sender(ctx) == PUBLISHER, 100);
        let chest = CHEST
        {
            id: object::new(ctx),
            chestBalance: balance::zero(),
            owner: ctx.sender(),
        };
        transfer::share_object(chest);
    }

    public fun topup(chest: &mut CHEST, mut coins: Coin<SUI>)
    {
        coin::put(&mut chest.chestBalance, coins);
    }

    public fun withdraw(chest: &mut CHEST, amount: u64, ctx: &mut TxContext)
    {
        assert!(tx_context::sender(ctx) == chest.owner, 1);
        assert!(amount > 0, 6); // Sıfırdan büyük miktar kontrolü
        assert!(chest.chestBalance.value() >= amount, 7); // Yeterli bakiye kontrolü

        // Chest'ten belirtilen miktarı çek
        let coin = coin::take(&mut chest.chestBalance, amount, ctx);

        // Kullanıcıya gönder
        transfer::public_transfer(coin, chest.owner);
    }

    /// Kullanıcı commit gönderir
    public entry fun commit( chest: &mut CHEST, mut user_coin: Coin<SUI>, commit_hash: vector<u8>, input: u8, ctx: &mut TxContext)
    {
        let amount = coin::value(&user_coin);
        assert!(amount >= 100_000_000, 2);
        assert!(input >= 2 && input <= 8, 3);

        // Coin'i chest'e koy
        coin::put(&mut chest.chestBalance, user_coin);

        let commit = Commit
        {
            id: object::new(ctx),
            player: tx_context::sender(ctx),
            commit_hash,
            amount,
            input,
        };
        transfer::transfer(commit, tx_context::sender(ctx));
    }

    /// Kullanıcı secret'ı reveal eder ve random sonucu hesaplanır
    public entry fun reveal(r: &Random, chest: &mut CHEST, commit: Commit, secret: vector<u8>, ctx: &mut TxContext)
    {
        assert!(commit.player == tx_context::sender(ctx), 10);

        // Hash doğrulama (SHA3-256 std::hash altındadır)
        let expected_hash = hash::sha3_256(secret);
        assert!(expected_hash == commit.commit_hash, 11);

        // Random üretim: mix yok; doğrudan generator kullan
        let mut generator = random::new_generator(r, ctx);
        let result = random::generate_u8_in_range(&mut generator, 1, commit.input);

        if (result != 1)
        {
            let input_u64 = commit.input as u64;
            let mut factor = ((100000000 / (10000 - (10000 / input_u64))) - 10000) / 100;
            factor = factor - 3;
            let bonus = (commit.amount * factor) / 100;

            // Balance kontrolü (doğru kullanım)
            assert!(balance::value(&chest.chestBalance) >= bonus, 4);

            // Ödeme
            let pay_bonus = coin::take(&mut chest.chestBalance, bonus, ctx);
            let mut refund = coin::take(&mut chest.chestBalance, commit.amount, ctx);
            coin::join(&mut refund, pay_bonus);
            transfer::public_transfer(refund, commit.player);
            event::emit(DiceValue { value:result });
        }
        else
        {
            event::emit(DiceValue { value:result });
        };
        let Commit { id, player: _, commit_hash: _, amount: _, input: _ } = commit;
        object::delete(id);

    }
}
using Ali.Helper;
using NBitcoin;
using OpenDive.BCS;
using Org.BouncyCastle.Ocsp;
using SHA3.Net;
using Sui.Accounts;
using Sui.Rpc;
using Sui.Rpc.Client;
using Sui.Rpc.Models;
using Sui.Transactions;
using Sui.Types;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net.Sockets;
using System.Numerics;
using System.Security.Cryptography;
using System.Threading.Tasks;
using Unity.Mathematics;
using Unity.VisualScripting;
using UnityEditor.PackageManager;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UIElements;

public class SuiManager : GenericSingleton<SuiManager>
{
    SuiClient _client;
    Account _account;

    [SerializeField] private string _deathCardPackageId = "";
    [SerializeField] private string _deathCardChestId = "";
    private string _deathCardModuleFunc = "::lucky_game::play";
    private string _deathCardCommitFunc = "::lucky_game::commit";
    private string _deathCardRevealFunc = "::lucky_game::reveal";
    private string _randomPackageId = "0x8";

    SuiStructTag _sui_coin = new SuiStructTag("0x2::sui::SUI");
    SuiStructTag _coin = new SuiStructTag("0x2::coin::Coin");

    [HideInInspector]
    public UnityEvent OnDeathCardStarted = new UnityEvent();
    [HideInInspector]
    public UnityEvent<int> OnDeathCardEnded = new UnityEvent<int>();
    [HideInInspector]
    public UnityEvent<float> OnBalanceUpdated = new UnityEvent<float>();
    [HideInInspector]
    public UnityEvent<float> OnBalanceChanged = new UnityEvent<float>();
    private void Start()
    {
        _client = new SuiClient(Constants.TestnetConnection);
        _account = new Account("****");
        //Debug.Log(_account.PrivateKey.KeyHex);
        //Debug.Log(_account.SuiAddress().KeyHex);
        CheckBalance();
        Test();
    }

    async void Test()
    {
        //System.Guid guid = System.Guid.NewGuid();
        //byte[] secret = guid.ToByteArray();
        //HashAlgorithm halg = Sha3.Create();
        //byte[] computedHash = halg.ComputeHash(secret);
        //Debug.Log("Ok => " + guid.ToString());
        //string commitAddress = await Commit(0.1m, 8, computedHash);
        //await Reveal(commitAddress, secret);

        System.Guid guid = new System.Guid("a8eb7e10-a909-4962-a69f-6d977646a1fa");
        HashAlgorithm halg = Sha3.Create();
        byte[] secret = guid.ToByteArray();
        byte[] computedHash = halg.ComputeHash(secret);
        await Reveal("0xe7bb117a7ab3f4091c1c12ac20b6961a788f43a0687e8ef0da8b0fc893834d0d", secret);
    }

    float GetFloatFromBigInteger(BigInteger value)
    {
        decimal suiValue = (decimal)value / 1_000_000_000m;
        return (float)suiValue;
    }

    async Task CheckBalance()
    {
        RpcResult<Balance> result = await _client.GetBalanceAsync(_account);
        BigInteger balance = result.Result.TotalBalance;
        decimal suiBalance = (decimal)balance / 1_000_000_000m;
        OnBalanceUpdated?.Invoke((float)suiBalance);
    }

    public async void DeathCard(float betAmount, byte cardCount)
    {
        OnDeathCardStarted?.Invoke();
       
        
        int diceValue = await PlayDeathCard((decimal)betAmount, cardCount);
        OnDeathCardEnded?.Invoke(diceValue);
        //await CheckBalance();
    }

    async Task<List<CoinDetails>> GetSUICoins()
    {
        List<CoinDetails> result = new List<CoinDetails>();
        RpcResult<CoinPage> alice_sui_coins = await _client.GetCoinsAsync(_account, _sui_coin);
        for (int i = 0; i < alice_sui_coins.Result.Data.Length; i++)
        {
            result.Add(alice_sui_coins.Result.Data[i]);
        }
        return result;
    }

    async Task<string> Commit(decimal amount, byte cardCount, byte[] commitHash)
    {
        Debug.Log("Bet Amount => " + amount);
        TransactionBlock tx_block = new TransactionBlock();
        ulong requestedAmountLong = (ulong)(amount * 1_000_000_000m);
        List<TransactionArgument> splitArgs = tx_block.AddMoveCallTx
        (
            SuiMoveNormalizedStructType.FromStr("0x2::coin::split"),
            new SerializableTypeTag[] { new SerializableTypeTag("0x2::sui::SUI") },
            new TransactionArgument[]
            {
                tx_block.gas,  // Insert coin object ID here
                tx_block.AddPure(new U64(requestedAmountLong)) // Insert split amount here
            }
        );
        tx_block.AddMoveCallTx
        (
            SuiMoveNormalizedStructType.FromStr($"{_deathCardPackageId}{_deathCardCommitFunc}"),
            new SerializableTypeTag[] { },
            new TransactionArgument[]
            {
                    tx_block.AddObjectInput(_deathCardChestId),
                    splitArgs[0],
                    tx_block.AddPure(new Bytes(commitHash)),
                    tx_block.AddPure(new U8(cardCount))
            }
        );
        TransactionBlockResponseOptions transactionBlockResponseOptions = new TransactionBlockResponseOptions();
        transactionBlockResponseOptions.ShowEvents = true;
        transactionBlockResponseOptions.ShowBalanceChanges = true;
        transactionBlockResponseOptions.ShowEffects = true;
        transactionBlockResponseOptions.ShowObjectChanges = true;
        RpcResult<TransactionBlockResponse> result_task = await _client.SignAndExecuteTransactionBlockAsync
        (
            tx_block,
            _account,
            transactionBlockResponseOptions
        );
        

        if (result_task.Error != null)
        {
            Debug.Log("PlayDeathCard Error => " + result_task.Error.Message);
            return "";
        }
        else if(result_task.Result != null && result_task.Result.ObjectChanges != null)
        {
            for (int i = 0; i < result_task.Result.ObjectChanges.Length; i++) 
            {
                ObjectChange oc = result_task.Result.ObjectChanges[i];
                if(oc.Type == ObjectChangeType.Created)
                {
                    Debug.Log(oc.Change.ToString());
                    return oc.Change.ToString();
                }
            }
        }
            
        return "";

    }

    async Task<int> Reveal(string commitAddress, byte[] secret)
    {
        TransactionBlock tx_block = new TransactionBlock();
        tx_block.AddMoveCallTx
        (
            SuiMoveNormalizedStructType.FromStr($"{_deathCardPackageId}{_deathCardRevealFunc}"),
            new SerializableTypeTag[] { new SerializableTypeTag("0xd83656ac7864ec1581b1cc2b0ee37f0e6e7b95a27a2edfe7fe2d6d14927b1f51::lucky_game::Commit") },
            new TransactionArgument[]
            {
                    tx_block.AddObjectInput(_randomPackageId),
                    tx_block.AddObjectInput(_deathCardChestId),
                    tx_block.AddObjectInput(commitAddress),
                    tx_block.AddPure(new Bytes(secret))
            }
        );

        TransactionBlockResponseOptions transactionBlockResponseOptions = new TransactionBlockResponseOptions();
        transactionBlockResponseOptions.ShowEvents = true;
        transactionBlockResponseOptions.ShowBalanceChanges = true;
        transactionBlockResponseOptions.ShowEffects = true;
        transactionBlockResponseOptions.ShowObjectChanges = true;
        Debug.Log(tx_block.Serialize().ToString());
        RpcResult<TransactionBlockResponse> result_task = await _client.SignAndExecuteTransactionBlockAsync
        (
            tx_block,
            _account,
            transactionBlockResponseOptions
        );

        if (result_task.Error != null)
        {
            Debug.Log("Reveal Error => " + result_task.Error.Message);
            return -1;
        }

        if (result_task.Result != null && result_task.Result.BalanceChanges != null && result_task.Result.BalanceChanges.Length > 0)
        {
            BigInteger changeAmountBig = result_task.Result.BalanceChanges[0].Amount;
            float changeAmount = GetFloatFromBigInteger(changeAmountBig);
            OnBalanceChanged?.Invoke(GetFloatFromBigInteger(result_task.Result.BalanceChanges[0].Amount));
        }

        try
        {
            foreach (var m_event in result_task.Result.Events)
            {
                if (m_event.Type.Contains("DiceValue"))
                {
                    string value = m_event.ParsedJson.GetValue("value").ToString();
                    return int.Parse(value);
                }
            }
            return -1;
        }
        catch (System.Exception ex)
        {
            Debug.LogError(ex.StackTrace);
            return -1;
        }


    }

    async Task<int> PlayDeathCard(decimal amount, byte cardCount)
    {
        Debug.Log("Bet Amount => " + amount);
        TransactionBlock tx_block = new TransactionBlock();
        ulong requestedAmountLong = (ulong)(amount * 1_000_000_000m);
        List<TransactionArgument> splitArgs = tx_block.AddMoveCallTx
        (
            SuiMoveNormalizedStructType.FromStr("0x2::coin::split"),
            new SerializableTypeTag[] { new SerializableTypeTag("0x2::sui::SUI") },
            new TransactionArgument[]
            {
                tx_block.gas,  // Insert coin object ID here
                tx_block.AddPure(new U64(requestedAmountLong)) // Insert split amount here
            }
        );
        tx_block.AddMoveCallTx
        (
            SuiMoveNormalizedStructType.FromStr($"{_deathCardPackageId}{_deathCardModuleFunc}"),
            new SerializableTypeTag[] {  },
            new TransactionArgument[]
            {
                    tx_block.AddObjectInput(_randomPackageId),
                    tx_block.AddObjectInput(_deathCardChestId),
                    splitArgs[0],
                    tx_block.AddPure(new U8(cardCount))
            }
        );
        TransactionBlockResponseOptions transactionBlockResponseOptions = new TransactionBlockResponseOptions();
        transactionBlockResponseOptions.ShowEvents = true;
        transactionBlockResponseOptions.ShowBalanceChanges = true;
        transactionBlockResponseOptions.ShowEffects = true;
        transactionBlockResponseOptions.ShowObjectChanges = true;
        RpcResult<TransactionBlockResponse> result_task = await _client.SignAndExecuteTransactionBlockAsync
        (
            tx_block,
            _account,
            transactionBlockResponseOptions
        );

        if (result_task.Error != null)
        {
            Debug.Log("PlayDeathCard Error => " + result_task.Error.Message);
            return -1;
        }

        if (result_task.Result != null && result_task.Result.BalanceChanges != null && result_task.Result.BalanceChanges.Length > 0)
        {
            BigInteger changeAmountBig = result_task.Result.BalanceChanges[0].Amount;
            float changeAmount = GetFloatFromBigInteger(changeAmountBig);
            OnBalanceChanged?.Invoke(GetFloatFromBigInteger(result_task.Result.BalanceChanges[0].Amount));
        }

        try
        {
            foreach (var m_event in result_task.Result.Events)
            {
                if(m_event.Type.Contains("DiceValue"))
                {
                    string value = m_event.ParsedJson.GetValue("value").ToString();
                    return int.Parse(value);
                }
            }
            return -1;
        }
        catch (System.Exception ex)
        {
            Debug.LogError(ex.StackTrace);
            return -1;
        }
        
        
    }

}

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions