diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 25ba854..851f5a2 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -11,7 +11,9 @@ "games": "Games", "showdown": "Showdown", "finger_guessing": "FingerGuessing", - "donate": "Donate" + "donate": "Donate", + "reset_auth": "reset auth", + "free_bank": "Free Bank" }, "issue_token": { diff --git a/public/locales/zh/translation.json b/public/locales/zh/translation.json index b8e5988..adaad0e 100644 --- a/public/locales/zh/translation.json +++ b/public/locales/zh/translation.json @@ -11,7 +11,9 @@ "games": "游戏", "showdown": "猜大小", "finger_guessing": "剪刀石头布", - "donate": "捐赠" + "donate": "捐赠", + "reset_auth": "重置私钥", + "free_bank": "无息存款" }, "issue_token": { diff --git a/src/components/Sidebar/game_menu.ts b/src/components/Sidebar/game_menu.ts index 77be822..31c43c0 100644 --- a/src/components/Sidebar/game_menu.ts +++ b/src/components/Sidebar/game_menu.ts @@ -13,7 +13,20 @@ const gameMenu = [ name: "finger_guessing", name_i18_key: "menu.finger_guessing", icon: VideogameAssetIcon, - path: "/game/finger_guessing" + path: "/game/finger-guessing" + }, + { + name: "free_bank", + name_i18_key: "menu.free_bank", + icon: VideogameAssetIcon, + path: "/game/free-bank" + }, + + { + name: "free_bank", + name_i18_key: "menu.reset_auth", + icon: VideogameAssetIcon, + path: "/game/reset-auth" }, ] diff --git a/src/games/free_bank/.gitignore b/src/games/free_bank/.gitignore new file mode 100644 index 0000000..cfcae93 --- /dev/null +++ b/src/games/free_bank/.gitignore @@ -0,0 +1,4 @@ +build +.DS_Store +.idea +release \ No newline at end of file diff --git a/src/games/free_bank/Move.toml b/src/games/free_bank/Move.toml new file mode 100644 index 0000000..6cade28 --- /dev/null +++ b/src/games/free_bank/Move.toml @@ -0,0 +1,11 @@ +[package] +name = "free_bank" +version = "0.0.1" + + +[addresses] +StarcoinFramework = "0x1" +admin = "0x68d69DC32Ae00470C8c96793A5C9b560" + +[dependencies] +StarcoinFramework = { git = "https://github.com/starcoinorg/starcoin-framework.git", rev = "cf1deda180af40a8b3e26c0c7b548c4c290cd7e7" } diff --git a/src/games/free_bank/change_auth.ts b/src/games/free_bank/change_auth.ts new file mode 100644 index 0000000..00a73f2 --- /dev/null +++ b/src/games/free_bank/change_auth.ts @@ -0,0 +1,119 @@ +import {bcs, utils} from "@starcoin/starcoin"; +import { hexlify} from "@ethersproject/bytes"; +import {getProvder} from "../../utils/stcWalletSdk"; +import {nodeUrlMap} from "../../utils/consts"; + +const ADMIN_ADDRESS = "0x68d69dc32ae00470c8c96793a5c9b560" + +export async function extract() { + try { + const functionId = `${ADMIN_ADDRESS}::ResetAuth::extract`; + const nodeUrl = nodeUrlMap[window.starcoin.networkVersion]; + const scriptFunction = await utils.tx.encodeScriptFunctionByResolve( + functionId, + [], + [], + nodeUrl + ); + + // Multiple BcsSerializers should be used in different closures, otherwise, the latter will be contaminated by the former. + const payloadInHex = (function () { + const se = new bcs.BcsSerializer(); + scriptFunction.serialize(se); + return hexlify(se.getBytes()); + })(); + + const txParams = { + data: payloadInHex, + }; + + const expiredSecs = 10; + if (expiredSecs > 0) { + // txParams.expiredSecs = expiredSecs + } + + const starcoinProvider = await getProvder(); + const transactionHash = await starcoinProvider + .getSigner() + .sendUncheckedTransaction(txParams); + window.console.log({transactionHash}); + } catch (e) { + window.console.error(e); + } +} + + +export async function restore() { + try { + const functionId = `${ADMIN_ADDRESS}::ResetAuth::restore`; + const nodeUrl = nodeUrlMap[window.starcoin.networkVersion]; + const scriptFunction = await utils.tx.encodeScriptFunctionByResolve( + functionId, + [], + [], + nodeUrl + ); + + // Multiple BcsSerializers should be used in different closures, otherwise, the latter will be contaminated by the former. + const payloadInHex = (function () { + const se = new bcs.BcsSerializer(); + scriptFunction.serialize(se); + return hexlify(se.getBytes()); + })(); + + const txParams = { + data: payloadInHex, + }; + + const expiredSecs = 10; + if (expiredSecs > 0) { + // txParams.expiredSecs = expiredSecs + } + + const starcoinProvider = await getProvder(); + const transactionHash = await starcoinProvider + .getSigner() + .sendUncheckedTransaction(txParams); + window.console.log({transactionHash}); + } catch (e) { + window.console.error(e); + } +} + + +export async function reset(authKey:string) { + try { + const functionId = `${ADMIN_ADDRESS}::ResetAuth::reset`; + const nodeUrl = nodeUrlMap[window.starcoin.networkVersion]; + const scriptFunction = await utils.tx.encodeScriptFunctionByResolve( + functionId, + [], + [authKey], + nodeUrl + ); + + // Multiple BcsSerializers should be used in different closures, otherwise, the latter will be contaminated by the former. + const payloadInHex = (function () { + const se = new bcs.BcsSerializer(); + scriptFunction.serialize(se); + return hexlify(se.getBytes()); + })(); + + const txParams = { + data: payloadInHex, + }; + + const expiredSecs = 10; + if (expiredSecs > 0) { + // txParams.expiredSecs = expiredSecs + } + + const starcoinProvider = await getProvder(); + const transactionHash = await starcoinProvider + .getSigner() + .sendUncheckedTransaction(txParams); + window.console.log({transactionHash}); + } catch (e) { + window.console.error(e); + } +} diff --git a/src/games/free_bank/index.ts b/src/games/free_bank/index.ts new file mode 100644 index 0000000..d13fd40 --- /dev/null +++ b/src/games/free_bank/index.ts @@ -0,0 +1,195 @@ +import {bcs, utils} from "@starcoin/starcoin"; +import { hexlify} from "@ethersproject/bytes"; +import {getProvder} from "../../utils/stcWalletSdk"; +import { nodeUrlMap} from "../../utils/consts"; + +const ADMIN_ADDRESS = "0x68d69dc32ae00470c8c96793a5c9b560" +export async function initBank() { + try { + const functionId = `${ADMIN_ADDRESS}::FreeBank::init`; + const nodeUrl = nodeUrlMap[window.starcoin.networkVersion]; + const scriptFunction = await utils.tx.encodeScriptFunctionByResolve( + functionId, + [], + [], + nodeUrl + ); + + // Multiple BcsSerializers should be used in different closures, otherwise, the latter will be contaminated by the former. + const payloadInHex = (function () { + const se = new bcs.BcsSerializer(); + scriptFunction.serialize(se); + return hexlify(se.getBytes()); + })(); + + const txParams = { + data: payloadInHex, + }; + + const expiredSecs = 10; + if (expiredSecs > 0) { + // txParams.expiredSecs = expiredSecs + } + + const starcoinProvider = await getProvder(); + const transactionHash = await starcoinProvider + .getSigner() + .sendUncheckedTransaction(txParams); + window.console.log({transactionHash}); + } catch (e) { + window.console.error(e); + } +} + +export async function extract() { + try { + const functionId = `${ADMIN_ADDRESS}::FreeBank::extract`; + const nodeUrl = nodeUrlMap[window.starcoin.networkVersion]; + const scriptFunction = await utils.tx.encodeScriptFunctionByResolve( + functionId, + [], + [], + nodeUrl + ); + + // Multiple BcsSerializers should be used in different closures, otherwise, the latter will be contaminated by the former. + const payloadInHex = (function () { + const se = new bcs.BcsSerializer(); + scriptFunction.serialize(se); + return hexlify(se.getBytes()); + })(); + + const txParams = { + data: payloadInHex, + }; + + const expiredSecs = 10; + if (expiredSecs > 0) { + // txParams.expiredSecs = expiredSecs + } + + const starcoinProvider = await getProvder(); + const transactionHash = await starcoinProvider + .getSigner() + .sendUncheckedTransaction(txParams); + window.console.log({transactionHash}); + } catch (e) { + window.console.error(e); + } +} + + +export async function restore() { + try { + const functionId = `${ADMIN_ADDRESS}::FreeBank::restore`; + const nodeUrl = nodeUrlMap[window.starcoin.networkVersion]; + const scriptFunction = await utils.tx.encodeScriptFunctionByResolve( + functionId, + [], + [], + nodeUrl + ); + + // Multiple BcsSerializers should be used in different closures, otherwise, the latter will be contaminated by the former. + const payloadInHex = (function () { + const se = new bcs.BcsSerializer(); + scriptFunction.serialize(se); + return hexlify(se.getBytes()); + })(); + + const txParams = { + data: payloadInHex, + }; + + const expiredSecs = 10; + if (expiredSecs > 0) { + // txParams.expiredSecs = expiredSecs + } + + const starcoinProvider = await getProvder(); + const transactionHash = await starcoinProvider + .getSigner() + .sendUncheckedTransaction(txParams); + window.console.log({transactionHash}); + } catch (e) { + window.console.error(e); + } +} + + +export async function deposit(amount: number) { + try { + const functionId = `${ADMIN_ADDRESS}::FreeBank::deposit`; + + const args = [amount]; + const nodeUrl = nodeUrlMap[window.starcoin.networkVersion]; + const scriptFunction = await utils.tx.encodeScriptFunctionByResolve( + functionId, + [], + args, + nodeUrl + ); + + // Multiple BcsSerializers should be used in different closures, otherwise, the latter will be contaminated by the former. + const payloadInHex = (function () { + const se = new bcs.BcsSerializer(); + scriptFunction.serialize(se); + return hexlify(se.getBytes()); + })(); + + const txParams = { + data: payloadInHex, + }; + + const expiredSecs = 10; + if (expiredSecs > 0) { + // txParams.expiredSecs = expiredSecs + } + + const starcoinProvider = await getProvder(); + const transactionHash = await starcoinProvider + .getSigner() + .sendUncheckedTransaction(txParams); + window.console.log({transactionHash}); + } catch (e) { + window.console.error(e); + } +} + +export async function withdraw(amount: number) { + try { + const functionId = `${ADMIN_ADDRESS}::FreeBank::withdraw`; + const args = [amount]; + const nodeUrl = nodeUrlMap[window.starcoin.networkVersion]; + const scriptFunction = await utils.tx.encodeScriptFunctionByResolve( + functionId, + [], + args, + nodeUrl + ); + + // Multiple BcsSerializers should be used in different closures, otherwise, the latter will be contaminated by the former. + const payloadInHex = (function () { + const se = new bcs.BcsSerializer(); + scriptFunction.serialize(se); + return hexlify(se.getBytes()); + })(); + + const txParams = { + data: payloadInHex, + }; + + const expiredSecs = 10; + if (expiredSecs > 0) { + // txParams.expiredSecs = expiredSecs + } + + const starcoinProvider = await getProvder(); + const transactionHash = await starcoinProvider + .getSigner() + .sendUncheckedTransaction(txParams); + window.console.log({transactionHash}); + } catch (e) { + window.console.error(e); + } +} diff --git a/src/games/free_bank/sources/FreeBank.move b/src/games/free_bank/sources/FreeBank.move new file mode 100644 index 0000000..746eb80 --- /dev/null +++ b/src/games/free_bank/sources/FreeBank.move @@ -0,0 +1,72 @@ +address admin { +module FreeBank { + use StarcoinFramework::Account; + use StarcoinFramework::Signer; + use StarcoinFramework::Token; + use StarcoinFramework::Math; + use StarcoinFramework::STC::STC; + const TOKEN_PRECISION: u8 = 9; + const INIT_MINT: u128 = 10000000000; + + struct FBC has drop, store, key {} + + struct FreeBank has store, key { + cap: Account::WithdrawCapability + } + + + public(script) fun init(signer: signer) { + Token::register_token(&signer, TOKEN_PRECISION); + Account::do_accept_token(&signer); + let scaling_factor = Math::pow(10, (TOKEN_PRECISION as u64)); + let token = Token::mint(&signer, INIT_MINT * scaling_factor); + Account::deposit_to_self(&signer, token); + } + + public(script) fun extract(signer: signer) { + let capability = Account::extract_withdraw_capability(&signer); + let fb = FreeBank { + cap: capability + }; + + move_to(&signer, fb); + } + + public(script) fun restore(signer: signer) acquires FreeBank { + let FreeBank { cap } = move_from(Signer::address_of(&signer)); + Account::restore_withdraw_capability(cap); + } + + + /// withdraw from bank + public(script) fun withdraw(signer: signer, amount: u128) acquires FreeBank { + let signer_address = Signer::address_of(&signer); + let capability = Account::extract_withdraw_capability(&signer); + Account::pay_from_capability(&capability, + @admin, + amount, x""); + Account::restore_withdraw_capability(capability); + + + let bank = borrow_global(@admin); + Account::pay_from_capability(&bank.cap, + Signer::address_of(&signer), + amount, x""); + } + + /// deposit amount to bank + public(script) fun deposit(signer: signer, amount: u128) acquires FreeBank { + let capability = Account::extract_withdraw_capability(&signer); + Account::pay_from_capability(&capability, + @admin, + amount, x""); + Account::restore_withdraw_capability(capability); + + + let bank = borrow_global(@admin); + Account::pay_from_capability(&bank.cap, + Signer::address_of(&signer), + amount, x""); + } +} +} \ No newline at end of file diff --git a/src/games/free_bank/sources/ResetAuth.move b/src/games/free_bank/sources/ResetAuth.move new file mode 100644 index 0000000..0fbca1f --- /dev/null +++ b/src/games/free_bank/sources/ResetAuth.move @@ -0,0 +1,30 @@ +address admin { +module ResetAuth { + use StarcoinFramework::Account; + use StarcoinFramework::Signer; + + struct Hold has store, key { + cap: Account::KeyRotationCapability + } + + public(script) fun reset(signer: signer, new_authentication_key: vector) { + let capability = Account::extract_key_rotation_capability(&signer); + Account::rotate_authentication_key_with_capability(&capability, new_authentication_key); + Account::restore_key_rotation_capability(capability); + } + + + public(script) fun extract(signer: signer) { + let capability = Account::extract_key_rotation_capability(&signer); + let fb = Hold { + cap: capability + }; + move_to(&signer, fb); + } + + public(script) fun restore(signer: signer) acquires Hold { + let Hold { cap } = move_from(Signer::address_of(&signer)); + Account::restore_key_rotation_capability(cap); + } +} +} \ No newline at end of file diff --git a/src/pages/Game/FreeBank/ChangeAuth.tsx b/src/pages/Game/FreeBank/ChangeAuth.tsx new file mode 100644 index 0000000..04b8964 --- /dev/null +++ b/src/pages/Game/FreeBank/ChangeAuth.tsx @@ -0,0 +1,51 @@ +import Card from "@mui/material/Card"; +import CardContent from "@mui/material/CardContent"; +import { + Stack, + TextField, +} from "@mui/material"; +import * as React from "react"; +import {useState} from "react"; +import CardActions from "@mui/material/CardActions"; +import Button from "@mui/material/Button"; + +import {reset, extract, restore} from "../../../games/free_bank/change_auth"; + +export default function ChangeAuth() { + const [authKey, setAuthKey] = useState("") + return <> + + + + + { + setAuthKey(v.target.value) + }} + multiline + rows={1}/> + + + + + + + + + + + + +} \ No newline at end of file diff --git a/src/pages/Game/FreeBank/index.tsx b/src/pages/Game/FreeBank/index.tsx new file mode 100644 index 0000000..ba9b22d --- /dev/null +++ b/src/pages/Game/FreeBank/index.tsx @@ -0,0 +1,88 @@ +import Card from "@mui/material/Card"; +import CardContent from "@mui/material/CardContent"; +import { + FormControl, InputLabel, MenuItem, Select, SelectChangeEvent, + Stack, + TextField, +} from "@mui/material"; +import * as React from "react"; +import {useState} from "react"; +import CardActions from "@mui/material/CardActions"; +import Button from "@mui/material/Button"; +import Box from "@mui/material/Box"; +import {deposit, extract, initBank, restore, withdraw} from "../../../games/free_bank"; +import {NANO_STC} from "../../../utils/consts"; + + +export default function FreeBank() { + let [amount, setAmount] = useState("1") + const [token, setToken] = useState("0x00000000000000000000000000000001::STC::STC") + const [tokenList] = useState(["0x00000000000000000000000000000001::STC::STC"]) + const handleChangeToken = (event: SelectChangeEvent) => { + setToken(event.target.value as string); + }; + + const handleInit = async () => { + await initBank() + }; + const handleWithdraw = async () => { + await withdraw(Number(amount) * NANO_STC) + }; + const handleDeposit = async () => { + await deposit(Number(amount) * NANO_STC) + }; + + return <> + + + + + + + Token + + + + + { + setAmount(v.target.value) + }} + multiline + rows={1}/> + + + + + + + + + + + + + + +} \ No newline at end of file diff --git a/src/router/router.tsx b/src/router/router.tsx index 8e29b1b..928e291 100644 --- a/src/router/router.tsx +++ b/src/router/router.tsx @@ -20,7 +20,8 @@ import FingerGuessingAdmin from "../pages/Game/FingerGuessing/FingerGuessingAdmi import GameFingerGuessing from "../pages/Game/FingerGuessing"; import Donate from "../pages/Donate"; - +import FreeBank from "../pages/Game/FreeBank"; +import ChangeAuth from "../pages/Game/FreeBank/ChangeAuth"; export default function Router() { return ( }/> @@ -39,8 +40,10 @@ export default function Router() { }/> }/> - }/> - }/> + }/> + }/> + }/> + }/> }/> }/>