Skip to content

Instantly share code, notes, and snippets.

@ayo-klaytn
Created November 27, 2024 07:50
Show Gist options
  • Save ayo-klaytn/2aad97e1e263b00f5403177a7ad1fff1 to your computer and use it in GitHub Desktop.
Save ayo-klaytn/2aad97e1e263b00f5403177a7ad1fff1 to your computer and use it in GitHub Desktop.
mergeInto(LibraryManager.library, {
InitializeWeb3: function() {
// Define ABI inside the function
window.ERC20_ABI = [
{
"inputs": [],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "mint",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
];
console.log("Checking dependencies...");
if (window.klaytn) {
// Network change listener
window.klaytn.on('networkChanged', async function(networkId) {
console.log("Network changed to:", networkId);
myGameInstance.SendMessage('Web3Manager', 'OnNetworkChanged', networkId.toString());
if (window.klaytn.selectedAddress) {
const web3 = new Web3(window.klaytn);
const tokenContract = new web3.eth.Contract(window.ERC20_ABI, "0x2CC89fc380EC1D7A601f4C95ccd783d207D08641");
const tokenBalance = await tokenContract.methods.balanceOf(window.klaytn.selectedAddress).call();
const tokenBalanceFormatted = web3.utils.fromWei(tokenBalance, 'ether');
myGameInstance.SendMessage('Web3Manager', 'OnTokenBalanceReceived', tokenBalanceFormatted);
}
});
// Account change listener
window.klaytn.on('accountsChanged', async function(accounts) {
console.log("Account changed:", accounts[0]);
if (accounts.length > 0) {
myGameInstance.SendMessage('Web3Manager', 'OnWalletConnected', accounts[0]);
const web3 = new Web3(window.klaytn);
const tokenContract = new web3.eth.Contract(window.ERC20_ABI, "0x2CC89fc380EC1D7A601f4C95ccd783d207D08641");
const tokenBalance = await tokenContract.methods.balanceOf(accounts[0]).call();
const tokenBalanceFormatted = web3.utils.fromWei(tokenBalance, 'ether');
myGameInstance.SendMessage('Web3Manager', 'OnTokenBalanceReceived', tokenBalanceFormatted);
}
});
myGameInstance.SendMessage('Web3Manager', 'OnWeb3Available', 'true');
} else {
myGameInstance.SendMessage('Web3Manager', 'OnWeb3Available', 'false');
}
},
ConnectWallet: function() {
console.log("Connect wallet called");
(async function() {
try {
const accounts = await window.klaytn.request({
method: 'eth_requestAccounts'
});
if (accounts && accounts.length > 0) {
const address = accounts[0];
console.log("Connected to address:", address);
// Send wallet connected event
myGameInstance.SendMessage('Web3Manager', 'OnWalletConnected', address);
// Get token balance
const web3 = new Web3(window.klaytn);
const tokenContract = new web3.eth.Contract(window.ERC20_ABI, "0x2CC89fc380EC1D7A601f4C95ccd783d207D08641");
const tokenBalance = await tokenContract.methods.balanceOf(address).call();
const tokenBalanceFormatted = web3.utils.fromWei(tokenBalance, 'ether');
console.log("Token balance:", tokenBalanceFormatted);
myGameInstance.SendMessage('Web3Manager', 'OnTokenBalanceReceived', tokenBalanceFormatted);
}
} catch (error) {
console.error("Connection error:", error);
myGameInstance.SendMessage('Web3Manager', 'OnError', error.message);
}
})();
},
GetTokenBalance: function(address) {
var addr = UTF8ToString(address);
console.log("Getting token balance for:", addr);
(async function() {
try {
const web3 = new Web3(window.klaytn);
const tokenContract = new web3.eth.Contract(window.ERC20_ABI, "0x2CC89fc380EC1D7A601f4C95ccd783d207D08641");
const balance = await tokenContract.methods.balanceOf(addr).call();
console.log("Raw token balance:", balance);
const balanceInTokens = web3.utils.fromWei(balance, 'ether');
console.log("Token balance:", balanceInTokens);
myGameInstance.SendMessage('Web3Manager', 'OnTokenBalanceReceived', balanceInTokens);
} catch (error) {
console.error("Token balance error:", error);
myGameInstance.SendMessage('Web3Manager', 'OnError', error.message);
}
})();
},
MintTokens: function(toAddress, amount) {
var to = UTF8ToString(toAddress).toLowerCase();
var amt = UTF8ToString(amount);
const CONTRACT_ADDRESS = "0x2CC89fc380EC1D7A601f4C95ccd783d207D08641";
(async function() {
try {
const web3 = new Web3(window.klaytn);
// Get current account
const accounts = await window.klaytn.request({
method: 'eth_requestAccounts'
});
const defaultAccount = accounts[0];
console.log("Minting from account:", defaultAccount);
const tokenContract = new web3.eth.Contract(window.ERC20_ABI, CONTRACT_ADDRESS);
// Convert and log amount
const amountInWei = web3.utils.toWei(amt, 'ether');
console.log("Minting details:", {
from: defaultAccount,
to: to,
amount: amt,
amountInWei: amountInWei
});
// Try getting balance before mint
const balanceBefore = await tokenContract.methods.balanceOf(to).call();
console.log("Balance before mint:", web3.utils.fromWei(balanceBefore, 'ether'));
const tx = {
// this could be provider.addresses[0] if it exists
from: defaultAccount,
// target address, this could be a smart contract address
to: CONTRACT_ADDRESS,
// this encodes the ABI of the method and the arguements
data: tokenContract.methods.mint(web3.utils.toChecksumAddress(to), amountInWei).encodeABI()
};
const receipt = await web3.eth.sendTransaction(tx)
console.log(receipt);
console.log("Mint successful:", receipt);
myGameInstance.SendMessage('Web3Manager', 'OnTokensMinted', receipt.transactionHash);
// Get new balance
const balanceAfter = await tokenContract.methods.balanceOf(to).call();
console.log("Balance after mint:", web3.utils.fromWei(balanceAfter, 'ether'));
} catch (error) {
console.error("Detailed mint error:", {
message: error.message,
code: error.code,
data: error.data,
stack: error.stack
});
// Try to decode the error
if (error.data) {
try {
const web3 = new Web3(window.klaytn);
const decodedError = web3.eth.abi.decodeParameter('string', error.data);
console.log("Decoded error:", decodedError);
} catch (e) {
console.log("Could not decode error data");
}
}
myGameInstance.SendMessage('Web3Manager', 'OnError',
`Mint failed: ${error.message.substring(0, 100)}...`);
}
})();
},
EnableWebGLCopyPaste: function(objectName) {
var gameObjectName = UTF8ToString(objectName);
// Add paste event listener to document
document.addEventListener('paste', function(e) {
var pastedText = e.clipboardData.getData('text');
console.log("Paste detected:", pastedText);
myGameInstance.SendMessage(gameObjectName, 'OnPasteReceived', pastedText);
});
}
});
using UnityEngine;
using System.Runtime.InteropServices;
using TMPro;
public class Web3Manager : MonoBehaviour
{
[SerializeField] private TextMeshProUGUI addressText;
[SerializeField] private TextMeshProUGUI tokenBalanceText;
[SerializeField] private TextMeshProUGUI networkText;
[SerializeField] private TextMeshProUGUI statusText;
[SerializeField] private TMP_InputField mintAmountInput;
[SerializeField] private TMP_InputField mintAddressInput;
private bool isWeb3Available = false;
private string currentAddress;
[DllImport("__Internal")]
private static extern void InitializeWeb3();
[DllImport("__Internal")]
private static extern void ConnectWallet();
[DllImport("__Internal")]
private static extern void GetTokenBalance(string address);
[DllImport("__Internal")]
private static extern void MintTokens(string toAddress, string amount);
[DllImport("__Internal")]
private static extern void EnableWebGLCopyPaste(string objectName);
void Start()
{
UpdateStatus("Initializing Web3...");
// Enable copy/paste for the address input
#if UNITY_WEBGL && !UNITY_EDITOR
EnableWebGLCopyPaste(gameObject.name);
#endif
#if UNITY_WEBGL && !UNITY_EDITOR
InitializeWeb3();
#endif
}
public void ConnectToWallet()
{
Debug.Log("ConnectToWallet called");
if (!isWeb3Available)
{
UpdateStatus("Please install Kaia Wallet!");
return;
}
UpdateStatus("Connecting to wallet...");
#if UNITY_WEBGL && !UNITY_EDITOR
ConnectWallet();
#endif
}
public void RefreshBalance()
{
if (string.IsNullOrEmpty(currentAddress))
{
UpdateStatus("No wallet connected");
return;
}
UpdateStatus("Refreshing balance...");
#if UNITY_WEBGL && !UNITY_EDITOR
GetTokenBalance(currentAddress);
#endif
}
public void OnWeb3Available(string available)
{
isWeb3Available = available.ToLower() == "true";
UpdateStatus(isWeb3Available ? "Ready to connect" : "Please install Kaia Wallet");
}
// Add this callback method to handle successful minting
public void OnTokensMinted(string transactionHash)
{
UpdateStatus($"Tokens minted successfully! Tx: {transactionHash}");
// Refresh the balance after minting
RefreshBalance();
}
// Add this public method to call from your UI button
public void MintTokens()
{
if (!isWeb3Available)
{
UpdateStatus("Please connect wallet first");
return;
}
string amount = mintAmountInput.text;
string toAddress = mintAddressInput.text;
// Validate inputs
if (string.IsNullOrEmpty(amount) || !decimal.TryParse(amount, out decimal amountValue) || amountValue <= 0)
{
UpdateStatus("Please enter a valid amount");
return;
}
if (string.IsNullOrEmpty(toAddress))
{
UpdateStatus("Please enter a destination address");
return;
}
UpdateStatus($"Minting {amount} tokens to {toAddress}...");
#if UNITY_WEBGL && !UNITY_EDITOR
// Just pass toAddress and amount, remove contract address parameter
MintTokens(toAddress, amount);
#endif
}
public void OnWalletConnected(string address)
{
currentAddress = address;
UpdateAddress(address);
UpdateStatus("Wallet connected!");
}
public void OnWalletDisconnected()
{
currentAddress = null;
UpdateAddress("Not Connected");
UpdateStatus("Wallet disconnected");
if (tokenBalanceText != null)
tokenBalanceText.text = "0 UTT";
}
public void OnPasteReceived(string pastedText)
{
if (mintAddressInput != null)
{
mintAddressInput.text = pastedText;
}
}
public void OnTokenBalanceReceived(string balance)
{
if (tokenBalanceText != null)
{
if (decimal.TryParse(balance, out decimal balanceValue))
{
tokenBalanceText.text = $"{balanceValue:F4} UTT";
UpdateStatus("Balance updated");
}
else
{
tokenBalanceText.text = "Error";
UpdateStatus("Error getting balance");
}
}
}
public void OnNetworkChanged(string networkId)
{
if (networkText != null)
{
string networkName = GetNetworkName(networkId);
networkText.text = networkName;
UpdateStatus($"Network changed to {networkName}");
}
}
public void OnError(string error)
{
Debug.LogError($"Web3 error: {error}");
UpdateStatus($"Error: {error}");
}
private void UpdateStatus(string message)
{
Debug.Log(message);
if (statusText != null)
statusText.text = message;
}
private void UpdateAddress(string address)
{
if (addressText != null)
addressText.text = address;
}
private string GetNetworkName(string networkId)
{
switch (networkId)
{
case "8217":
return "Kaia Mainnet";
case "1001":
return "Kairos Testnet";
default:
return $"Unknown Network ({networkId})";
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment