diff --git a/mover/Erikaibble/code/task5/Move.toml b/mover/Erikaibble/code/task5/Move.toml new file mode 100644 index 000000000..fef2bd16c --- /dev/null +++ b/mover/Erikaibble/code/task5/Move.toml @@ -0,0 +1,37 @@ +[package] +name = "task5" +edition = "2024.beta" # edition = "legacy" to use legacy (pre-2024) Move +# license = "" # e.g., "MIT", "GPL", "Apache 2.0" +# authors = ["..."] # e.g., ["Joe Smith (joesmith@noemail.com)", "John Snow (johnsnow@noemail.com)"] + +[dependencies] +Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/mainnet" } + +# For remote import, use the `{ git = "...", subdir = "...", rev = "..." }`. +# Revision can be a branch, a tag, and a commit hash. +# MyRemotePackage = { git = "https://some.remote/host.git", subdir = "remote/path", rev = "main" } + +# For local dependencies use `local = path`. Path is relative to the package root +# Local = { local = "../path/to" } + +# To resolve a version conflict and force a specific version for dependency +# override use `override = true` +# Override = { local = "../conflicting/version", override = true } + +[addresses] +task5 = "0x0" + +# Named addresses will be accessible in Move as `@name`. They're also exported: +# for example, `std = "0x1"` is exported by the Standard Library. +# alice = "0xA11CE" + +[dev-dependencies] +# The dev-dependencies section allows overriding dependencies for `--test` and +# `--dev` modes. You can introduce test-only dependencies here. +# Local = { local = "../path/to/dev-build" } + +[dev-addresses] +# The dev-addresses section allows overwriting named addresses for the `--test` +# and `--dev` modes. +# alice = "0xB0B" + diff --git a/mover/Erikaibble/code/task5/sources/task5.move b/mover/Erikaibble/code/task5/sources/task5.move new file mode 100644 index 000000000..98db3d00c --- /dev/null +++ b/mover/Erikaibble/code/task5/sources/task5.move @@ -0,0 +1,261 @@ +module task5::erikaibble_swap { + use sui::coin::{Self, Coin}; + use sui::balance::{Self, Supply, Balance}; + use sui::math; + + const EZeroAmount: u64 = 0; + + const EReservesEmpty: u64 = 2; + + const EPoolFull: u64 = 4; + + const FEE_SCALING: u128 = 10000; + + const FEE_PERCENT: u128 = 30; + + const MAX_POOL_VALUE: u64 = { + 18446744073709551615 / 10000 + }; + + public struct LSP has drop {} + + public struct Pool has key { + id: UID, + token_a: Balance, + token_b: Balance, + lsp_supply: Supply>, + /// Fee Percent is denominated in basis points. + fee_percent: u64 + } + + fun init(_: &mut TxContext) { + } + + entry fun create_pool ( + token_a: Coin, + token_b: Coin, + ctx: &mut TxContext + ) { + transfer::public_transfer( + create_pool_inner(token_a, token_b, ctx), + tx_context::sender(ctx) + ); + } + + fun create_pool_inner ( + token_a: Coin, + token_b: Coin, + ctx: &mut TxContext + ): Coin> { + let fee_percent = (FEE_PERCENT as u64); + + let token_a_amt = coin::value(&token_a); + let token_b_amt = coin::value(&token_b); + + assert!(token_a_amt > 0 && token_b_amt > 0, EZeroAmount); + assert!(token_a_amt < MAX_POOL_VALUE && token_b_amt < MAX_POOL_VALUE, EPoolFull); + + let share = math::sqrt(token_a_amt) * math::sqrt(token_b_amt); + let mut lsp_supply = balance::create_supply(LSP {}); + let lsp = balance::increase_supply(&mut lsp_supply, share); + + transfer::share_object(Pool { + id: object::new(ctx), + token_a: coin::into_balance(token_a), + token_b: coin::into_balance(token_b), + lsp_supply, + fee_percent, + }); + + coin::from_balance(lsp, ctx) + } + + entry fun add_liquidity ( + pool: &mut Pool, + token_a: Coin, + token_b: Coin, + ctx: &mut TxContext + ) { + transfer::public_transfer( + add_liquidity_inner(pool, token_a, token_b, ctx), + tx_context::sender(ctx) + ) + } + + fun add_liquidity_inner ( + pool: &mut Pool, + token_a: Coin, + token_b: Coin, + ctx: &mut TxContext + ): Coin> { + assert!(coin::value(&token_a) > 0 && coin::value(&token_b) > 0, EZeroAmount); + + let (token_a_amt, token_b_amt, lsp_supply) = get_amounts(pool); + assert!(token_a_amt > 0 && token_b_amt > 0, EReservesEmpty); + + let token_a_balance = coin::into_balance(token_a); + let token_b_balance = coin::into_balance(token_b); + + let token_a_added = balance::value(&token_a_balance); + let token_b_added = balance::value(&token_b_balance); + + let share_minted = math::min( + (token_a_added * lsp_supply) / token_a_amt, + (token_b_added * lsp_supply) / token_b_amt + ); + + let token_a_amt = balance::join(&mut pool.token_a, token_a_balance); + let token_b_amt = balance::join(&mut pool.token_b, token_b_balance); + + assert!(token_a_amt < MAX_POOL_VALUE && token_b_amt < MAX_POOL_VALUE, EPoolFull); + + let balance = balance::increase_supply(&mut pool.lsp_supply, share_minted); + + coin::from_balance(balance, ctx) + } + + entry fun remove_liquidity ( + pool: &mut Pool, + lsp: Coin>, + ctx: &mut TxContext + ) { + let (token_a, token_b) = remove_liquidity_inner(pool, lsp, ctx); + let sender = tx_context::sender(ctx); + + transfer::public_transfer(token_a, sender); + transfer::public_transfer(token_b, sender); + } + + fun remove_liquidity_inner ( + pool: &mut Pool, + lsp: Coin>, + ctx: &mut TxContext + ): (Coin, Coin) { + let lsp_amount = coin::value(&lsp); + assert!(lsp_amount > 0, EZeroAmount); + + let (token_a_amt, token_b_amt, total_supply) = get_amounts(pool); + + let token_a = (token_a_amt * lsp_amount) / total_supply; + let token_b = (token_b_amt * lsp_amount) / total_supply; + + balance::decrease_supply(&mut pool.lsp_supply, coin::into_balance(lsp)); + + ( + coin::take(&mut pool.token_a, token_a, ctx), + coin::take(&mut pool.token_b, token_b, ctx), + ) + } + + entry fun swap_a_to_b ( + pool: &mut Pool, + token_a: Coin, + ctx: &mut TxContext + ) { + transfer::public_transfer( + swap_a_to_b_inner(pool, token_a, ctx), + tx_context::sender(ctx) + ) + } + + fun swap_a_to_b_inner ( + pool: &mut Pool, + token_a: Coin, + ctx: &mut TxContext + ): Coin { + let token_a_amt = coin::value(&token_a); + assert!(token_a_amt > 0, EZeroAmount); + + let (token_a_amt, token_b_amt, _) = get_amounts(pool); + assert!(token_a_amt > 0 && token_b_amt > 0, EReservesEmpty); + + let token_b_amt = sell_token_a(pool, token_a_amt); + + balance::join(&mut pool.token_a, coin::into_balance(token_a)); + + coin::take(&mut pool.token_b, token_b_amt, ctx) + } + + entry fun swap_b_to_a ( + pool: &mut Pool, + token_b: Coin, + ctx: &mut TxContext + ) { + transfer::public_transfer( + swap_b_to_a_inner(pool, token_b, ctx), + tx_context::sender(ctx) + ) + } + + fun swap_b_to_a_inner ( + pool: &mut Pool, + token_b: Coin, + ctx: &mut TxContext + ): Coin { + let token_b_amt = coin::value(&token_b); + assert!(token_b_amt > 0, EZeroAmount); + + let (token_a_amt, token_b_amt, _) = get_amounts(pool); + assert!(token_a_amt > 0 && token_b_amt > 0, EReservesEmpty); + + let token_a_amt = sell_token_b(pool, token_b_amt); + + balance::join(&mut pool.token_b, coin::into_balance(token_b)); + + coin::take(&mut pool.token_a, token_a_amt, ctx) + } + + public fun sell_token_a(pool: &Pool, to_sell: u64): u64 { + let (token_a_amt, token_b_amt, _) = get_amounts(pool); + calc_output_amount( + to_sell, + token_a_amt, + token_b_amt, + pool.fee_percent + ) + } + + public fun sell_token_b(pool: &Pool, to_sell: u64): u64 { + let (token_a_amt, token_b_amt, _) = get_amounts(pool); + calc_output_amount( + to_sell, + token_b_amt, + token_a_amt, + pool.fee_percent + ) + } + + public fun get_amounts(pool: &Pool): (u64, u64, u64) { + ( + balance::value(&pool.token_a), + balance::value(&pool.token_b), + balance::supply_value(&pool.lsp_supply), + ) + } + + public fun calc_output_amount( + input_amount: u64, + input_reserve: u64, + output_reserve: u64, + fee_percent: u64 + ): u64 { + let ( + input_amount, + input_reserve, + output_reserve, + fee_percent + ) = ( + (input_amount as u128), + (input_reserve as u128), + (output_reserve as u128), + (fee_percent as u128), + ); + + let input_with_fee = input_amount * FEE_SCALING / (FEE_SCALING - fee_percent); + + let total = input_reserve * output_reserve; + let output_amount = output_reserve - total / (input_reserve + input_with_fee); + + (output_amount as u64) + } +} diff --git a/mover/Erikaibble/code/task6/index.html b/mover/Erikaibble/code/task6/index.html new file mode 100644 index 000000000..3c9258df8 --- /dev/null +++ b/mover/Erikaibble/code/task6/index.html @@ -0,0 +1,12 @@ + + + + + + SDK Task + + +
+ + + diff --git a/mover/Erikaibble/code/task6/package.json b/mover/Erikaibble/code/task6/package.json new file mode 100644 index 000000000..076a96228 --- /dev/null +++ b/mover/Erikaibble/code/task6/package.json @@ -0,0 +1,24 @@ +{ + "name": "with-vite", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite --port 3000" + }, + "dependencies": { + "@mysten/sui.js": "0.51.2", + "@suiet/wallet-kit": "0.2.24", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "tweetnacl": "^1.0.3", + "navi-sdk": "^1.1.21" + }, + "devDependencies": { + "@types/react": "^18.0.22", + "@types/react-dom": "^18.0.7", + "@vitejs/plugin-react": "^2.2.0", + "typescript": "^5.4.3", + "vite": "^3.2.7" + } +} diff --git a/mover/Erikaibble/code/task6/src/App.css b/mover/Erikaibble/code/task6/src/App.css new file mode 100644 index 000000000..ac87691e7 --- /dev/null +++ b/mover/Erikaibble/code/task6/src/App.css @@ -0,0 +1,43 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +.card { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 2em; +} + +.read-the-docs { + color: #888; +} + +.btn-group button+button{ + margin-left: 12px; +} \ No newline at end of file diff --git a/mover/Erikaibble/code/task6/src/App.tsx b/mover/Erikaibble/code/task6/src/App.tsx new file mode 100644 index 000000000..85feb5eac --- /dev/null +++ b/mover/Erikaibble/code/task6/src/App.tsx @@ -0,0 +1,114 @@ +import "./App.css"; +import { + ConnectButton, + useAccountBalance, + useWallet, + ErrorCode, + formatSUI, +} from "@suiet/wallet-kit"; +import "@suiet/wallet-kit/style.css"; +import { TransactionBlock } from "@mysten/sui.js/transactions"; +import {depositCoin, borrowCoin} from 'navi-sdk/dist/libs/PTB' +import { Pool, PoolConfig } from "navi-sdk/dist/types"; +import { pool } from 'navi-sdk/dist/address' +import {Sui, USDC} from 'navi-sdk/dist/address'; + +function App() { + const wallet = useWallet(); + const { balance } = useAccountBalance(); + + function uint8arrayToHex(value: Uint8Array | undefined) { + if (!value) return ""; + // @ts-ignore + return value.toString("hex"); + } + + async function handleNAVISDK() { + if (!wallet.account) return; + const account = wallet.account; + const sender = account.address; + try { + let txb = new TransactionBlock(); + txb.setSender(sender); + + let getCoinInfo = balance; + if (!getCoinInfo) { + throw new Error("Insufficient balance"); + } + + let deposit_amount = 1e9; + let sui_symbol = Sui.symbol; + const pool_sui: PoolConfig = pool[sui_symbol as keyof Pool]; + const [to_deposit] = txb.splitCoins(txb.gas, [deposit_amount]); + await depositCoin(txb, pool_sui, to_deposit, deposit_amount); + + let borrow_amount = BigInt(Math.round(0.072311 * 1e6)); + let usdc_symbol = USDC.symbol; + const pool_usdc: PoolConfig = pool[usdc_symbol as keyof Pool]; + const [borrow_coin] = await borrowCoin(txb, pool_usdc, borrow_amount); + + await depositCoin(txb, pool_usdc, borrow_coin, borrow_amount); + + const resData = await wallet.signAndExecuteTransactionBlock({ + transactionBlock: txb, + }); + console.log("transaction digest: " + JSON.stringify(resData)); + } catch (e) { + console.error("failed", e); + alert("failed (see response in the console)"); + } + } + + return ( +
+
+ { + if (error.code === ErrorCode.WALLET__CONNECT_ERROR__USER_REJECTED) { + console.warn( + "user rejected the connection to " + error.details?.wallet + ); + } else { + console.warn("unknown connect error: ", error); + } + }} + /> + + {!wallet.connected ? ( +

Connect wallet from now!

+ ) : ( +
+
+

current wallet: {wallet.adapter?.name}

+

+ wallet status:{" "} + {wallet.connecting + ? "connecting" + : wallet.connected + ? "connected" + : "disconnected"} +

+

wallet address: {wallet.account?.address}

+

current network: {wallet.chain?.name}

+

+ wallet balance:{" "} + {formatSUI(balance ?? 0, { + withAbbr: false, + })}{" "} + SUI +

+

+ wallet publicKey: {uint8arrayToHex(wallet.account?.publicKey)} +

+
+
+ +
+
+ )} +
+
+ ); +} + +export default App; diff --git a/mover/Erikaibble/code/task6/src/index.css b/mover/Erikaibble/code/task6/src/index.css new file mode 100644 index 000000000..41bc8b138 --- /dev/null +++ b/mover/Erikaibble/code/task6/src/index.css @@ -0,0 +1,64 @@ +:root { + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} + + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/mover/Erikaibble/code/task6/src/main.tsx b/mover/Erikaibble/code/task6/src/main.tsx new file mode 100644 index 000000000..b27eb00c3 --- /dev/null +++ b/mover/Erikaibble/code/task6/src/main.tsx @@ -0,0 +1,16 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App' +import './index.css' + +import { + WalletProvider, +} from '@suiet/wallet-kit'; + +ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( + + + + + +) diff --git a/mover/Erikaibble/code/task6/vite.config.ts b/mover/Erikaibble/code/task6/vite.config.ts new file mode 100644 index 000000000..55ef63119 --- /dev/null +++ b/mover/Erikaibble/code/task6/vite.config.ts @@ -0,0 +1,18 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], + build: { + target: 'es2020' + }, + optimizeDeps: { + esbuildOptions: { + target: 'es2020', + define: { + global: 'globalThis', + }, + }, + }, +}) diff --git a/mover/Erikaibble/img/scan4.png b/mover/Erikaibble/img/scan4.png new file mode 100644 index 000000000..c4ee0df18 Binary files /dev/null and b/mover/Erikaibble/img/scan4.png differ diff --git a/mover/Erikaibble/readme.md b/mover/Erikaibble/readme.md index 893f202f3..8f433a353 100644 --- a/mover/Erikaibble/readme.md +++ b/mover/Erikaibble/readme.md @@ -38,17 +38,17 @@ - [x] play game hash: 5R4WmD4CY2YCg8Scpbzqebb5EqJTaEi9pgWAcKPtEyiV ## 05 Move Swap -- [] swap package id : -- [] call swap CoinA-> CoinB hash : -- [] call swap CoinB-> CoinA hash : +- [x] swap package id : 0x125e446c3486cb5ee0e61078e12cff4f6ac5923a7daff3b1a2a0102e09693600 +- [x] call swap CoinA-> CoinB hash : H4VnSqsPuapZTj31fNXGbU9RmFjj5wNUNSuErFqUXgbd +- [x] call swap CoinB-> CoinA hash : 6nwFqfepFENe3omnL7dmig11qABxVEwsAxHLftiaKUjw ## 06 Dapp-kit SDK PTB -- [] save hash : +- [x] save hash : 6MVpKVjB1EK22UkiMHLcnTSiFrpFRmvXU2txz6bad9bd ## 07 Move CTF Check In -- [] CLI call 截图 : ![截图](./images/你的图片地址) -- [] flag hash : +- [x] CLI call 截图 : ![截图](./img/scan4.png) +- [x] flag hash : GVD8feG3RjJZ6anYrvkhN8zjprmokTPQh9buZwYFmKnN ## 08 Move CTF Lets Move -- [] proof : -- [] flag hash : +- [x] proof : ae49485f6941f67f180a +- [x] flag hash : Ae2i7T34sXmnERhQW9dDdUgN5Vjf8RDHncNRUDQFvRc9