diff --git a/infra/rooch-portal-v2/src/sections/inviter/index.tsx b/infra/rooch-portal-v2/src/sections/inviter/index.tsx index 3c389e966e..60f6ef206c 100644 --- a/infra/rooch-portal-v2/src/sections/inviter/index.tsx +++ b/infra/rooch-portal-v2/src/sections/inviter/index.tsx @@ -2,6 +2,7 @@ import { useEffect } from 'react'; +import { isValidAddress } from '@roochnetwork/rooch-sdk'; import { useRouter } from '../../routes/hooks'; import { INVITER_ADDRESS_KEY } from '../../utils/inviter'; @@ -9,10 +10,10 @@ export function InviterView({ inviterAddress }: { inviterAddress?: string }) { const router = useRouter(); useEffect(() => { - if (inviterAddress) { + if (inviterAddress && isValidAddress(inviterAddress)) { window.localStorage.setItem(INVITER_ADDRESS_KEY, inviterAddress); - router.push(`/settings`); } + router.push('/settings'); }, [inviterAddress, router]); return <>; diff --git a/infra/rooch-portal-v2/src/sections/settings/view.tsx b/infra/rooch-portal-v2/src/sections/settings/view.tsx index 259dec660b..1ed93db52a 100644 --- a/infra/rooch-portal-v2/src/sections/settings/view.tsx +++ b/infra/rooch-portal-v2/src/sections/settings/view.tsx @@ -3,7 +3,14 @@ import axios from 'axios'; import { useState, useEffect, useCallback } from 'react'; import { CopyToClipboard } from 'react-copy-to-clipboard'; -import { Args, toHEX, Transaction, stringToBytes } from '@roochnetwork/rooch-sdk'; +import { + Args, + toHEX, + Transaction, + stringToBytes, + BitcoinAddress, + isValidRoochAddress, +} from '@roochnetwork/rooch-sdk'; import { useRoochClient, SessionKeyGuard, @@ -14,7 +21,16 @@ import { } from '@roochnetwork/rooch-sdk-kit'; import { LoadingButton } from '@mui/lab'; -import { Card , Chip, Stack, Button, TextField, CardHeader, Typography, CardContent } from '@mui/material'; +import { + Card, + Chip, + Stack, + Button, + TextField, + CardHeader, + Typography, + CardContent, +} from '@mui/material'; import { sleep } from 'src/utils/common'; @@ -36,7 +52,7 @@ export function SettingsView() { const wallet = useCurrentWallet(); const faucetUrl = useNetworkVariable('faucet').url; const twitterOracleAddress = useNetworkVariable('roochMultiSigAddr'); - const inviter = useNetworkVariable('inviter') + const inviter = useNetworkVariable('inviter'); const [tweetStatus, setTweetStatus] = useState(''); const [twitterId, setTwitterId] = useState(); const [verifying, setVerifying] = useState(false); @@ -172,8 +188,12 @@ export function SettingsView() { const sign = await wallet.wallet?.sign(stringToBytes('utf8', signMsg)); const pk = wallet.wallet!.getPublicKey().toBytes(); + const inviterRoochAddr = isValidRoochAddress(inviterAddr) + ? inviterAddr + : new BitcoinAddress(inviterAddr).genRoochAddress().toStr(); + const payload = JSON.stringify({ - inviter: inviterAddr, + inviter: inviterRoochAddr, tweet_id: pureTweetId, claimer_sign: toHEX(sign!), public_key: toHEX(pk), @@ -204,7 +224,7 @@ export function SettingsView() { // step 2, check inviter const inviterAddr = window.localStorage.getItem(INVITER_ADDRESS_KEY); - if (inviterAddr && inviterAddr !== '') { + if (inviterAddr && inviterAddr !== '' && inviterAddr !== address?.toStr()) { // check invite is open const result = await client.queryObjectStates({ filter: { diff --git a/infra/rooch-portal-v2/src/utils/inviter.ts b/infra/rooch-portal-v2/src/utils/inviter.ts index 07b5676ea3..46493bc911 100644 --- a/infra/rooch-portal-v2/src/utils/inviter.ts +++ b/infra/rooch-portal-v2/src/utils/inviter.ts @@ -1,17 +1,18 @@ -import type { NetworkType, ThirdPartyAddress } from "@roochnetwork/rooch-sdk" +import type { NetworkType, ThirdPartyAddress } from '@roochnetwork/rooch-sdk'; -export const INVITER_ADDRESS_KEY = 'inviter-address' +export const INVITER_ADDRESS_KEY = 'inviter-address'; export const getTwitterShareText = (network: NetworkType, address?: ThirdPartyAddress) => { - const networkText = network === 'mainnet' ? 'PreMainnet' : 'Testnet' - return `BTC:${address?.toStr()} + const networkText = network === 'mainnet' ? 'PreMainnet' : 'Testnet'; + return `BTC:${address?.toStr()} -Rooch ${networkText} is live! Bind your Twitter to earn RGas, and visit https://${network === 'mainnet' ? '':'test-'}grow.rooch.network to earn rewards with your BTC. +Rooch ${networkText} is live! Bind your Twitter to earn RGas, and visit https://${network === 'mainnet' ? '' : 'test-'}grow.rooch.network to earn rewards with your BTC. Join Rooch: ${getShareLink(network, address)} -#RoochNetwork #${networkText}` -} +#RoochNetwork #${networkText}`; +}; -export const getShareLink = (network: NetworkType, address?: ThirdPartyAddress) => `https://${network === 'mainnet' ? '':'test-'}portal.rooch.network/inviter/${address?.genRoochAddress().toBech32Address()}` \ No newline at end of file +export const getShareLink = (network: NetworkType, address?: ThirdPartyAddress) => + `https://${network === 'mainnet' ? '' : 'test-'}portal.rooch.network/inviter/${address?.toStr()}`; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e7e5b4ea9d..19a355fd67 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -204,7 +204,7 @@ importers: version: 5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mui/material-nextjs': specifier: ^5.15.11 - version: 5.16.6(@emotion/cache@11.13.1)(@mui/material@5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.5)(next@14.2.11(@babel/core@7.25.2)(@playwright/test@1.47.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + version: 5.16.6(@emotion/cache@11.13.1)(@mui/material@5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.5)(next@14.2.11(@babel/core@7.25.2)(@playwright/test@1.47.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) '@mui/x-data-grid': specifier: ^7.7.0 version: 7.16.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@mui/material@5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -228,7 +228,7 @@ importers: version: 0.2.9(typescript@5.6.2) '@roochnetwork/rooch-sdk-kit': specifier: 0.2.10 - version: 0.2.10(@tanstack/react-query@5.56.2(react@18.3.1))(@types/react-dom@18.3.0)(@types/react@18.3.5)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + version: 0.2.10(@tanstack/react-query@5.56.2(react@18.3.1))(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) '@tanstack/react-query': specifier: ^5.51.11 version: 5.56.2(react@18.3.1) @@ -264,7 +264,7 @@ importers: version: 11.5.4(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) next: specifier: ^14.2.4 - version: 14.2.11(@babel/core@7.25.2)(@playwright/test@1.47.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 14.2.11(@babel/core@7.25.2)(@playwright/test@1.47.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) nprogress: specifier: ^0.2.0 version: 0.2.0 @@ -13284,11 +13284,11 @@ snapshots: '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) '@types/react': 18.3.5 - '@mui/material-nextjs@5.16.6(@emotion/cache@11.13.1)(@mui/material@5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.5)(next@14.2.11(@babel/core@7.25.2)(@playwright/test@1.47.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + '@mui/material-nextjs@5.16.6(@emotion/cache@11.13.1)(@mui/material@5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.5)(next@14.2.11(@babel/core@7.25.2)(@playwright/test@1.47.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 '@mui/material': 5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - next: 14.2.11(@babel/core@7.25.2)(@playwright/test@1.47.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 14.2.11(@babel/core@7.25.2)(@playwright/test@1.47.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 optionalDependencies: '@emotion/cache': 11.13.1 @@ -14983,7 +14983,7 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.21.3': optional: true - '@roochnetwork/rooch-sdk-kit@0.2.10(@tanstack/react-query@5.56.2(react@18.3.1))(@types/react-dom@18.3.0)(@types/react@18.3.5)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)': + '@roochnetwork/rooch-sdk-kit@0.2.10(@tanstack/react-query@5.56.2(react@18.3.1))(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)': dependencies: '@radix-ui/react-dialog': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-dropdown-menu': 2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -21214,7 +21214,7 @@ snapshots: postcss: 8.4.14 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - styled-jsx: 5.1.1(@babel/core@7.25.2)(babel-plugin-macros@3.1.0)(react@18.3.1) + styled-jsx: 5.1.1(@babel/core@7.25.2)(react@18.3.1) watchpack: 2.4.0 zod: 3.21.4 optionalDependencies: @@ -21231,7 +21231,7 @@ snapshots: - '@babel/core' - babel-plugin-macros - next@14.2.11(@babel/core@7.25.2)(@playwright/test@1.47.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + next@14.2.11(@babel/core@7.25.2)(@playwright/test@1.47.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@next/env': 14.2.11 '@swc/helpers': 0.5.5 @@ -21241,7 +21241,7 @@ snapshots: postcss: 8.4.31 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - styled-jsx: 5.1.1(@babel/core@7.25.2)(babel-plugin-macros@3.1.0)(react@18.3.1) + styled-jsx: 5.1.1(@babel/core@7.25.2)(react@18.3.1) optionalDependencies: '@next/swc-darwin-arm64': 14.2.11 '@next/swc-darwin-x64': 14.2.11 @@ -22980,13 +22980,12 @@ snapshots: dependencies: inline-style-parser: 0.1.1 - styled-jsx@5.1.1(@babel/core@7.25.2)(babel-plugin-macros@3.1.0)(react@18.3.1): + styled-jsx@5.1.1(@babel/core@7.25.2)(react@18.3.1): dependencies: client-only: 0.0.1 react: 18.3.1 optionalDependencies: '@babel/core': 7.25.2 - babel-plugin-macros: 3.1.0 stylis-plugin-rtl@2.1.1(stylis@4.3.4): dependencies: diff --git a/sdk/typescript/rooch-sdk/test-e2e/case/grow.test.ts b/sdk/typescript/rooch-sdk/test-e2e/case/grow.test.ts new file mode 100644 index 0000000000..800b534bac --- /dev/null +++ b/sdk/typescript/rooch-sdk/test-e2e/case/grow.test.ts @@ -0,0 +1,165 @@ +// Copyright (c) RoochNetwork +// SPDX-License-Identifier: Apache-2.0 + +import { beforeAll, describe, it, afterAll } from 'vitest' +import { TestBox } from '../setup.js' +import { Args, bcs } from '../../src/bcs/index.js' +import { BitcoinAddress, BitcoinNetowkType, fromHEX, getRoochNodeUrl, RoochAddress, StateKVView } from "../../src"; + +describe('Checkpoints Transfer API', () => { + let testBox: TestBox + + beforeAll(async () => { + testBox = TestBox.setup(getRoochNodeUrl('mainnet')) + }) + + afterAll(async () => { + testBox.cleanEnv() + }) + + type VoterInfo = { + voteTime: number + address: string + value: number + } + + it('export all votes', async () => { + let votes: VoterInfo[] = [] + + // find 2025-1-5-20 the closest deal + // const txResult = await testBox.getClient().queryTransactions({ + // filter: { + // time_range: { + // start_time: (1736078340*1000).toString(), + // end_time: (1736078400 * 1000).toString() + // } + // } + // }) + // + // txResult.data.sort((a, b) => Number(a.transaction.sequence_info.tx_timestamp) - Number(b.transaction.sequence_info.tx_timestamp)) + // tx order 72730361 + // tx hash 0x54adf5ddc03b1083f9146645e1d7ecaa5b272a66c449a96ec82f38073713c382 + // state root 0x42d23fdaaf5ec6cd62c7f6f2ba527e397ea3d45d8c1f9d5956054aa8b8122271 + const getAllVoters = async (table: string, cursor?: string) => { + const result = await testBox.getClient().listStates({ + accessPath: `/table/${table}`, + stateOption: { + stateRoot: '0x42d23fdaaf5ec6cd62c7f6f2ba527e397ea3d45d8c1f9d5956054aa8b8122271', + }, + cursor, + limit: '200', + }) + + const items = result.data.map((item) => { + //0x292467201e8b21f9188f722df170854e97681353da66ec16586817abaad96c36a301000000000000000000000000000000000000000000000000000000000000 + const d = bcs.struct('xxx', { + address: bcs.bytes(32), + value: bcs.u256(), + }) + + const view = item.state.value + const decode = d.parse(fromHEX(view)) + return { + voteTime: Number(item.state.created_at), + address: new RoochAddress(decode.address).toHexAddress(), + value: Number(decode.value), + } + }) + votes = votes.concat(items).sort((a, b) => b.value - a.value) + if (result.has_next_page) { + await getAllVoters(table, result.next_cursor || undefined) + } else { + } + } + + await getAllVoters('0x8b9d2a598a1f2d9cbdec84a479360afb8431ef5b19f871bf36d9de8ad19d9041') + + const endTime = 1736078400 * 1000 + votes = votes.filter((item) => item.voteTime < endTime) + + const with1000 = votes.slice(0, 1000) + + const resultAddressMap = await testBox.getClient().executeViewFunction({ + target: '0x3::address_mapping::resolve_bitcoin_batch', + args: [ + Args.vec( + 'address', + with1000.map((item) => item.address), + ), + ], + }) + + const warpWith1000 = with1000.map((item, i) => { + const tmp = (resultAddressMap.return_values![0].decoded_value as any).value[i][0] + const btcAddress = new BitcoinAddress(tmp, BitcoinNetowkType.Bitcoin).toStr() + return { + ...item, + btcAddress, + roochAddress: new RoochAddress(item.address).toBech32Address(), + } + }) + + const json = JSON.stringify(warpWith1000) + + console.log(json) + }) + + it('export all register', async () => { + // const result = await testBox.getClient().queryObjectStates({ + // filter: { + // object_type: '0x701c21bf1c8cd5af8c42983890d8ca55e7a820171b8e744c13f2d9998bf76cc3::grow_registration::Registration' + // } + // }) + + //main 0xa1e5d1779e9764839a0526e35d5972d28584d3dcfac1d2305e231b6490a59740 + //test 0xa21c5dd091454a554bacde8210bfe0aed7d392af04b04d26a155c8f8cfeac9dc + let votes: StateKVView[] = [] + let nextCursor: string | undefined | null = undefined + while (true) { + const result = await testBox.getClient().listStates({ + accessPath: '/table/0xa1e5d1779e9764839a0526e35d5972d28584d3dcfac1d2305e231b6490a59740', + limit: '200', + cursor: nextCursor + }) + nextCursor = result.next_cursor + votes = votes.concat(result.data) + + if (!result.has_next_page) { + break + } + } + + const formatVotes = votes.map((item) => { + const view = item.state.decoded_value!.value! as any + return { + address: view.name, + roochAddress: new RoochAddress(view.name).toBech32Address(), + voteCount: view.value!.value!.amount, + recipientAddress: view.value!.value!.register_info, + } + }) + + const resultAddressMap = await testBox.getClient().executeViewFunction({ + target: '0x3::address_mapping::resolve_bitcoin_batch', + args: [ + Args.vec( + 'address', + formatVotes.map((item) => item.address), + ), + ], + }) + + const finalVotes = formatVotes.map((item, i) => { + const tmp = (resultAddressMap.return_values![0].decoded_value as any).value[i][0] + const btcAddress = new BitcoinAddress(tmp, BitcoinNetowkType.Bitcoin).toStr() + return { + ...item, + btcAddress: btcAddress, + } + }) + + const json = JSON.stringify(finalVotes) + + console.log(json) + }) +}) diff --git a/sdk/typescript/rooch-sdk/test-e2e/setup.ts b/sdk/typescript/rooch-sdk/test-e2e/setup.ts index 40b84f475e..5944f2d2d1 100644 --- a/sdk/typescript/rooch-sdk/test-e2e/setup.ts +++ b/sdk/typescript/rooch-sdk/test-e2e/setup.ts @@ -16,15 +16,15 @@ export class TestBox extends TestBoxA { private client: RoochClient keypair: Secp256k1Keypair - constructor(keypair: Secp256k1Keypair) { + constructor(keypair: Secp256k1Keypair, url?: string) { super() this.keypair = keypair - this.client = new RoochClient({ url: DEFAULT_NODE_URL }) + this.client = new RoochClient({ url: url || DEFAULT_NODE_URL }) } - static setup(): TestBox { + static setup(url?: string): TestBox { const kp = Secp256k1Keypair.generate() - return new TestBox(kp) + return new TestBox(kp, url) } async loadRoochEnv(