Skip to content

Instantly share code, notes, and snippets.

@Rizary
Created February 14, 2023 01:30
Show Gist options
  • Save Rizary/779568d03165234c8bb7680b76f4f3c7 to your computer and use it in GitHub Desktop.
Save Rizary/779568d03165234c8bb7680b76f4f3c7 to your computer and use it in GitHub Desktop.
//go:build !js && !wasm
// +build !js,!wasm
package zcncrypto
import (
"bytes"
"encoding/hex"
"fmt"
"time"
"github.com/0chain/errors"
"github.com/tyler-smith/go-bip39"
"github.com/0chain/gosdk/core/encryption"
)
func init() {
}
//HerumiScheme - a signature scheme for BLS0Chain Signature
type HerumiScheme struct {
PublicKey string `json:"public_key"`
PrivateKey string `json:"private_key"`
Mnemonic string `json:"mnemonic"`
id ID
Ids string `json:"threshold_scheme_id"`
}
//NewHerumiScheme - create a MiraclScheme object
func NewHerumiScheme() *HerumiScheme {
return &HerumiScheme{
id: BlsSignerInstance.NewID(),
}
}
// GenerateKeys generate fresh keys
func (b0 *HerumiScheme) GenerateKeys() (*Wallet, error) {
return b0.generateKeys("0chain-client-split-key")
}
// GenerateKeysWithEth generate fresh keys based on eth wallet
func (b0 *HerumiScheme) GenerateKeysWithEth(mnemonic, password string) (*Wallet, error) {
if len(mnemonic) == 0 {
return nil, fmt.Errorf("Mnemonic phase is mandatory.")
}
b0.Mnemonic = mnemonic
_, err := bip39.NewSeedWithErrorChecking(b0.Mnemonic, password)
if err != nil {
return nil, fmt.Errorf("Wrong mnemonic phase.")
}
return b0.generateKeys(password)
}
// RecoverKeys recovery keys from mnemonic
func (b0 *HerumiScheme) RecoverKeys(mnemonic string) (*Wallet, error) {
if mnemonic == "" {
return nil, errors.New("recover_keys", "Set mnemonic key failed")
}
if b0.PublicKey != "" || b0.PrivateKey != "" {
return nil, errors.New("recover_keys", "Cannot recover when there are keys")
}
b0.Mnemonic = mnemonic
return b0.GenerateKeys()
}
func (b0 *HerumiScheme) GetMnemonic() string {
if b0 == nil {
return ""
}
return b0.Mnemonic
}
//SetPrivateKey set private key to sign
func (b0 *HerumiScheme) SetPrivateKey(privateKey string) error {
if b0.PublicKey != "" {
return errors.New("set_private_key", "cannot set private key when there is a public key")
}
if b0.PrivateKey != "" {
return errors.New("set_private_key", "private key already exists")
}
b0.PrivateKey = privateKey
//ToDo: b0.publicKey should be set here?
return nil
}
func (b0 *HerumiScheme) GetPrivateKey() string {
return b0.PrivateKey
}
func (b0 *HerumiScheme) SplitKeys(numSplits int) (*Wallet, error) {
if b0.PrivateKey == "" {
return nil, errors.New("split_keys", "primary private key not found")
}
primaryFr := BlsSignerInstance.NewFr()
primarySk := BlsSignerInstance.NewSecretKey()
err := primarySk.DeserializeHexStr(b0.PrivateKey)
if err != nil {
return nil, err
}
err = primaryFr.SetLittleEndian(primarySk.GetLittleEndian())
if err != nil {
return nil, err
}
// New Wallet
w := &Wallet{}
w.Keys = make([]KeyPair, numSplits)
sk := BlsSignerInstance.NewSecretKey()
for i := 0; i < numSplits-1; i++ {
tmpSk := BlsSignerInstance.NewSecretKey()
tmpSk.SetByCSPRNG()
w.Keys[i].PrivateKey = tmpSk.SerializeToHexStr()
pub := tmpSk.GetPublicKey()
w.Keys[i].PublicKey = pub.SerializeToHexStr()
sk.Add(tmpSk)
}
aggregateSk := BlsSignerInstance.NewFr()
err = aggregateSk.SetLittleEndian(sk.GetLittleEndian())
if err != nil {
return nil, err
}
//Subtract the aggregated private key from the primary private key to derive the last split private key
lastSk := BlsSignerInstance.NewFr()
BlsSignerInstance.FrSub(lastSk, primaryFr, aggregateSk)
// Last key
lastSecretKey := BlsSignerInstance.NewSecretKey()
err = lastSecretKey.SetLittleEndian(lastSk.Serialize())
if err != nil {
return nil, err
}
w.Keys[numSplits-1].PrivateKey = lastSecretKey.SerializeToHexStr()
w.Keys[numSplits-1].PublicKey = lastSecretKey.GetPublicKey().SerializeToHexStr()
// Generate client ID and public
w.ClientKey = primarySk.GetPublicKey().SerializeToHexStr()
w.ClientID = encryption.Hash(primarySk.GetPublicKey().Serialize())
w.Mnemonic = b0.Mnemonic
w.Version = CryptoVersion
w.DateCreated = time.Now().Format(time.RFC3339)
return w, nil
}
//Sign sign message
func (b0 *HerumiScheme) Sign(hash string) (string, error) {
sig, err := b0.rawSign(hash)
if err != nil {
return "", err
}
return sig.SerializeToHexStr(), nil
}
//SetPublicKey - implement interface
func (b0 *HerumiScheme) SetPublicKey(publicKey string) error {
if b0.PrivateKey != "" {
return errors.New("set_public_key", "cannot set public key when there is a private key")
}
if b0.PublicKey != "" {
return errors.New("set_public_key", "public key already exists")
}
b0.PublicKey = publicKey
return nil
}
//GetPublicKey - implement interface
func (b0 *HerumiScheme) GetPublicKey() string {
return b0.PublicKey
}
//Verify - implement interface
func (b0 *HerumiScheme) Verify(signature, msg string) (bool, error) {
if b0.PublicKey == "" {
return false, errors.New("verify", "public key does not exists for verification")
}
sig := BlsSignerInstance.NewSignature()
pk := BlsSignerInstance.NewPublicKey()
err := sig.DeserializeHexStr(signature)
if err != nil {
return false, err
}
rawHash, err := hex.DecodeString(msg)
if err != nil {
return false, err
}
if rawHash == nil {
return false, errors.New("verify", "failed hash while signing")
}
err = pk.DeserializeHexStr(b0.PublicKey)
if err != nil {
return false, err
}
return sig.Verify(pk, string(rawHash)), nil
}
func (b0 *HerumiScheme) Add(signature, msg string) (string, error) {
sign := BlsSignerInstance.NewSignature()
err := sign.DeserializeHexStr(signature)
if err != nil {
return "", err
}
signature1, err := b0.rawSign(msg)
if err != nil {
return "", errors.Wrap(err, "BLS signing failed")
}
sign.Add(signature1)
return sign.SerializeToHexStr(), nil
}
// GetPrivateKeyAsByteArray - converts private key into byte array
func (b0 *HerumiScheme) GetPrivateKeyAsByteArray() ([]byte, error) {
if len(b0.PrivateKey) == 0 {
return nil, errors.New("get_private_key_as_byte_array", "cannot convert empty private key to byte array")
}
privateKeyBytes, err := hex.DecodeString(b0.PrivateKey)
if err != nil {
return nil, err
}
return privateKeyBytes, nil
}
//SetID sets ID in HexString format
func (b0 *HerumiScheme) SetID(id string) error {
if b0.id == nil {
b0.id = BlsSignerInstance.NewID()
}
b0.Ids = id
return b0.id.SetHexString(id)
}
//GetID gets ID in hex string format
func (b0 *HerumiScheme) GetID() string {
if b0.id == nil {
b0.id = BlsSignerInstance.NewID()
}
return b0.id.GetHexString()
}
func (b0 *HerumiScheme) generateKeys(password string) (*Wallet, error) {
// Check for recovery
if len(b0.Mnemonic) == 0 {
entropy, err := bip39.NewEntropy(256)
if err != nil {
return nil, errors.Wrap(err, "Generating entropy failed")
}
b0.Mnemonic, err = bip39.NewMnemonic(entropy)
if err != nil {
return nil, errors.Wrap(err, "Generating mnemonic failed")
}
}
// Generate a Bip32 HD wallet for the mnemonic and a user supplied password
seed := bip39.NewSeed(b0.Mnemonic, password)
r := bytes.NewReader(seed)
BlsSignerInstance.SetRandFunc(r)
// New Wallet
w := &Wallet{}
w.Keys = make([]KeyPair, 1)
// Generate pair
sk := BlsSignerInstance.NewSecretKey()
sk.SetByCSPRNG()
w.Keys[0].PrivateKey = sk.SerializeToHexStr()
pub := sk.GetPublicKey()
w.Keys[0].PublicKey = pub.SerializeToHexStr()
b0.PrivateKey = w.Keys[0].PrivateKey
b0.PublicKey = w.Keys[0].PublicKey
w.ClientKey = w.Keys[0].PublicKey
w.ClientID = encryption.Hash(pub.Serialize())
w.Mnemonic = b0.Mnemonic
w.Version = CryptoVersion
w.DateCreated = time.Now().Format(time.RFC3339)
// Revert the Random function to default
BlsSignerInstance.SetRandFunc(nil)
return w, nil
}
func (b0 *HerumiScheme) rawSign(hash string) (Signature, error) {
sk := BlsSignerInstance.NewSecretKey()
if b0.PrivateKey == "" {
return nil, errors.New("raw_sign", "private key does not exists for signing")
}
rawHash, err := hex.DecodeString(hash)
if err != nil {
return nil, err
}
if rawHash == nil {
return nil, errors.New("raw_sign", "failed hash while signing")
}
sk.SetByCSPRNG()
err = sk.DeserializeHexStr(b0.PrivateKey)
if err != nil {
return nil, err
}
sig := sk.Sign(string(rawHash))
return sig, nil
}
package encryption
import (
"crypto/sha1"
"encoding/hex"
"golang.org/x/crypto/sha3"
)
const HASH_LENGTH = 32
type HashBytes [HASH_LENGTH]byte
/*Hash - hash the given data and return the hash as hex string */
func Hash(data interface{}) string {
return hex.EncodeToString(RawHash(data))
}
/*RawHash - Logic to hash the text and return the hash bytes */
func RawHash(data interface{}) []byte {
var databuf []byte
switch dataImpl := data.(type) {
case []byte:
databuf = dataImpl
case HashBytes:
databuf = dataImpl[:]
case string:
databuf = []byte(dataImpl)
default:
panic("unknown type")
}
hash := sha3.New256()
hash.Write(databuf)
var buf []byte
return hash.Sum(buf)
}
/*FastHash - sha1 hash the given data and return the hash as hex string */
func FastHash(data interface{}) string {
return hex.EncodeToString(RawFastHash(data))
}
/*RawFastHash - Logic to sha1 hash the text and return the hash bytes */
func RawFastHash(data interface{}) []byte {
var databuf []byte
switch dataImpl := data.(type) {
case []byte:
databuf = dataImpl
case HashBytes:
databuf = dataImpl[:]
case string:
databuf = []byte(dataImpl)
default:
panic("unknown type")
}
hash := sha1.New()
hash.Write(databuf)
var buf []byte
return hash.Sum(buf)
}
package encryption
import (
"bufio"
"io"
"strings"
"github.com/0chain/0box/code/core/common"
"github.com/0chain/0box/code/core/config"
. "github.com/0chain/0box/code/core/logging"
"github.com/0chain/gosdk/core/zcncrypto"
"github.com/herumi/bls-go-binary/bls"
)
/*ReadKeys - reads a publicKey and a privateKey from a Reader.
They are assumed to be in two separate lines one followed by the other*/
func ReadKeys(reader io.Reader) (publicKey, privateKey, publicIp string) {
scanner := bufio.NewScanner(reader)
scanner.Scan()
publicKey = scanner.Text()
scanner.Scan()
privateKey = scanner.Text()
scanner.Scan()
publicIp = scanner.Text()
return publicKey, privateKey, publicIp
}
// Verify - given a public key and a signature and the hash used to create the signature, verify the signature
func Verify(publicKey, signature, hash string) (bool, error) {
publicKey = MiraclToHerumiPK(publicKey)
signature = MiraclToHerumiSig(signature)
signScheme := zcncrypto.NewSignatureScheme(config.Configuration.SignatureScheme)
if signScheme != nil {
err := signScheme.SetPublicKey(publicKey)
if err != nil {
return false, err
}
return signScheme.Verify(signature, hash)
}
return false, common.NewError("invalid_signature_scheme", "Invalid signature scheme. Please check configuration")
}
// If input is normal herumi/bls public key, it returns it immmediately.
// So this is completely backward compatible with herumi/bls.
// If input is MIRACL public key, convert it to herumi/bls public key.
//
// This is an example of the raw public key we expect from MIRACL
var miraclExamplePK = `0418a02c6bd223ae0dfda1d2f9a3c81726ab436ce5e9d17c531ff0a385a13a0b491bdfed3a85690775ee35c61678957aaba7b1a1899438829f1dc94248d87ed36817f6dfafec19bfa87bf791a4d694f43fec227ae6f5a867490e30328cac05eaff039ac7dfc3364e851ebd2631ea6f1685609fc66d50223cc696cb59ff2fee47ac`
//
// This is an example of the same MIRACL public key serialized with ToString().
// pk ([1bdfed3a85690775ee35c61678957aaba7b1a1899438829f1dc94248d87ed368,18a02c6bd223ae0dfda1d2f9a3c81726ab436ce5e9d17c531ff0a385a13a0b49],[039ac7dfc3364e851ebd2631ea6f1685609fc66d50223cc696cb59ff2fee47ac,17f6dfafec19bfa87bf791a4d694f43fec227ae6f5a867490e30328cac05eaff])
func MiraclToHerumiPK(pk string) string {
if len(pk) != len(miraclExamplePK) {
// If input is normal herumi/bls public key, it returns it immmediately.
return pk
}
n1 := pk[2:66]
n2 := pk[66:(66 + 64)]
n3 := pk[(66 + 64):(66 + 64 + 64)]
n4 := pk[(66 + 64 + 64):(66 + 64 + 64 + 64)]
var p bls.PublicKey
err := p.SetHexString("1 " + n2 + " " + n1 + " " + n4 + " " + n3)
if err != nil {
Logger.Error("MiraclToHerumiPK: " + err.Error())
}
return p.SerializeToHexStr()
}
// Converts signature 'sig' to format that the herumi/bls library likes.
// zwallets are using MIRACL library which send a MIRACL signature not herumi
// lib.
//
// If the 'sig' was not in MIRACL format, we just return the original sig.
const miraclExampleSig = `(0d4dbad6d2586d5e01b6b7fbad77e4adfa81212c52b4a0b885e19c58e0944764,110061aa16d5ba36eef0ad4503be346908d3513c0a2aedfd0d2923411b420eca)`
func MiraclToHerumiSig(sig string) string {
if len(sig) <= 2 {
return sig
}
if sig[0] != miraclExampleSig[0] {
return sig
}
withoutParens := sig[1:(len(sig) - 1)]
comma := strings.Index(withoutParens, ",")
if comma < 0 {
return "00"
}
n1 := withoutParens[0:comma]
n2 := withoutParens[(comma + 1):]
var sign bls.Sign
err := sign.SetHexString("1 " + n1 + " " + n2)
if err != nil {
Logger.Error("MiraclToHerumiSig: " + err.Error())
}
return sign.SerializeToHexStr()
}
//go:build !js && !wasm
// +build !js,!wasm
package zcncrypto
import (
"encoding/hex"
"encoding/json"
"fmt"
"github.com/0chain/errors"
)
// NewSignatureScheme creates an instance for using signature functions
func NewSignatureScheme(sigScheme string) SignatureScheme {
switch sigScheme {
case "ed25519":
return NewED255190chainScheme()
case "bls0chain":
return NewHerumiScheme()
default:
panic(fmt.Sprintf("unknown signature scheme: %v", sigScheme))
}
}
// UnmarshalThresholdSignatureSchemes unmarshal SignatureScheme from json string
func UnmarshalSignatureSchemes(sigScheme string, obj interface{}) ([]SignatureScheme, error) {
switch sigScheme {
case "bls0chain":
if obj == nil {
return nil, nil
}
buf, err := json.Marshal(obj)
if err != nil {
return nil, err
}
var list []*HerumiScheme
if err := json.Unmarshal(buf, &list); err != nil {
return nil, err
}
ss := make([]SignatureScheme, len(list))
for i, v := range list {
// bls.ID from json
err = v.SetID(v.Ids)
if err != nil {
return nil, err
}
ss[i] = v
}
return ss, nil
default:
panic(fmt.Sprintf("unknown signature scheme: %v", sigScheme))
}
}
//GenerateThresholdKeyShares given a signature scheme will generate threshold sig keys
func GenerateThresholdKeyShares(t, n int, originalKey SignatureScheme) ([]SignatureScheme, error) {
b0ss, ok := originalKey.(*HerumiScheme)
if !ok {
return nil, errors.New("bls0_generate_threshold_key_shares", "Invalid encryption scheme")
}
b0original := BlsSignerInstance.NewSecretKey()
b0PrivateKeyBytes, err := b0ss.GetPrivateKeyAsByteArray()
if err != nil {
return nil, err
}
err = b0original.SetLittleEndian(b0PrivateKeyBytes)
if err != nil {
return nil, err
}
polynomial, err := b0original.GetMasterSecretKey(t)
if err != nil {
return nil, err
}
var shares []SignatureScheme
for i := 1; i <= n; i++ {
id := BlsSignerInstance.NewID()
err = id.SetDecString(fmt.Sprint(i))
if err != nil {
return nil, err
}
sk := BlsSignerInstance.NewSecretKey()
err = sk.Set(polynomial, id)
if err != nil {
return nil, err
}
share := &HerumiScheme{}
share.PrivateKey = hex.EncodeToString(sk.GetLittleEndian())
share.PublicKey = sk.GetPublicKey().SerializeToHexStr()
share.id = id
share.Ids = id.GetHexString()
shares = append(shares, share)
}
return shares, nil
}
import { useEffect, useRef, useState } from 'react'
import useDarkMode from 'use-dark-mode'
import clsx from 'clsx'
import { useSelector, useDispatch } from 'react-redux'
import useTranslation from 'next-translate/useTranslation'
import { useOnClickOutside, useEthAccount, useConnectWeb3 } from 'lib/hooks'
import MainContainer from '@/containers'
import BreadCrumbs from '@/components/breadcrumbs'
import TitleBox from '@/components/title-box'
import SearchBar from '@/components/search-bar'
import NftBox from '@/components/nft-box'
import Pagination from '@/components/pagination'
import LearnMore from '@/components/learn-more'
import ActivityTable from '@/components/activity-table'
import FilterDropdown from '@/components/filter-dropdown'
import ActionButton from '@/components/action-button'
import LoadingBox from 'components/loading-box'
import { selectActiveWallet } from 'store/wallet'
import UserIcon from '@/assets/svg/user-icon.svg'
import EthIcon from '@/assets/svg/eth-icon.svg'
import ZCNIcon from '@/assets/svg/zcn-token.svg'
import ActiveSaleIcon from '@/assets/svg/active-sale-icon.svg'
import PublicMintIcon from '@/assets/svg/public-mint-icon.svg'
import PackMintIcon from '@/assets/svg/pack-icon.svg'
import EditIcon from '@/assets/svg/edit-icon.svg'
import Refresh from '@/assets/svg/refresh.svg'
import Share from '@/assets/svg/share.svg'
import Link from '@/assets/svg/link.svg'
import MoreOptions from '@/assets/svg/more-options.svg'
import CubeIcon from '@/assets/svg/cube-icon.svg'
import { getNFTs, listAccountNftTokens } from 'store/nft'
import { getUserDetails, saveUserInfo } from 'store/user'
import {
basicGetRequestWithOptions,
getZeroBoxCsrf,
getTxnSignature,
} from 'store/api-utils'
import stl from './Profile.module.scss'
const Profile = () => {
const { t } = useTranslation('common')
const userNameRef = useRef()
const biographyRef = useRef()
const dropDownRef = useRef()
const dispatch = useDispatch()
const limit = 8
const activeWallet = useSelector(selectActiveWallet)
const [searchValue, setSearchValue] = useState('')
const [currentPage, setCurrentPage] = useState(1)
const [editUserName, setEditUserName] = useState(false)
const [editBiography, setEditBiography] = useState(false)
const [isActive, setIsActive] = useState(false)
const [loading, setLoading] = useState(true)
const [selectedSort, setSelectedSort] = useState()
const [userData, setUserData] = useState({})
const [userImages, setUserImages] = useState({})
const [message, setMessage] = useState('')
const [selectedFilters, setSelectedFilters] = useState([
'all',
'purchased',
'Owner Mint',
'minted',
'draft',
'Packs',
])
const [editMode, setEditMode] = useState(false)
const tokens = useSelector(state => state.nft.accountTokens)
const state = useSelector(state => state)
const { connect, connected } = useConnectWeb3()
const { account } = useEthAccount({ connected })
const userDetails = useSelector(state => state?.user?.userDetails)
const profilePicture = 'https://i.ibb.co/5MvQFMb/user-Profile-Picture.png'
const path = [
{
name: t('user-profile'),
link: '/profile',
},
]
const filterData = [
{
Icon: CubeIcon,
title: `${t('all')} ${t('nfts')}`,
active: 'all',
},
{
Icon: ActiveSaleIcon,
title: `${t('purchased')} ${t('nfts')}`,
active: 'purchased',
},
{
Icon: PublicMintIcon,
title: `${t('owner-mint')} ${t('nfts')}`,
active: 'Owner Mint',
},
{
title: `${t('minted')} ${t('nfts')}`,
active: 'minted',
},
{
title: `${t('draft')} ${t('nfts')}`,
active: 'draft',
},
{
Icon: PackMintIcon,
title: `${t('pack')} ${t('nfts')}`,
active: 'Packs',
},
]
const [filteredNftData, setFilteredNftData] = useState(tokens)
const [pageData, setPageData] = useState([])
useEffect(() => {
const { user } = state
const assetsParams = { user }
const activeWallet = selectActiveWallet(state)
const { id, keys } = activeWallet
const idToken = user.firebaseTokens.accessToken
const hashData = `${id}:${user.phoneNumber}:${keys.publicKey}`
;(async () => {
const { csrfToken } = await getZeroBoxCsrf(assetsParams)
const clientSignature = await getTxnSignature(hashData, keys.privateKey)
const options = {
method: 'GET',
headers: {
'X-App-Client-ID': id,
'X-App-Client-Key': keys.publicKey,
'X-App-Signature': clientSignature,
'X-App-Timestamp': new Date().getTime(),
'X-App-ID-TOKEN': idToken,
'X-App-Phone-Number': user.phoneNumber,
'X-CSRF-TOKEN': csrfToken,
'X-APP-TYPE': 'chalk',
},
}
const avatar =
userDetails?.avatar?.small_loc &&
(await basicGetRequestWithOptions({
url: `https://${userDetails?.avatar?.small_loc}`,
options,
}))
const bg_img =
userDetails?.bg_img?.small_loc &&
(await basicGetRequestWithOptions({
url: `https://${userDetails?.bg_img?.small_loc}`,
options,
}))
setUserData(prev => ({
...prev,
avatar: avatar?.image,
bg_img: bg_img?.image,
}))
})()
}, [userDetails])
useEffect(() => {
if (!connected) connect()
}, [connected, connect])
useEffect(() => {
setUserData({
appType: 'chalk',
userName: userDetails?.user_name,
biography: userDetails?.biography,
})
}, [userDetails])
useEffect(() => {
;(async () => {
if (account.address && tokens.length == 0) {
await dispatch(
listAccountNftTokens({
accountAddress: '0x0D77529fc2EB7B0bbeA0136ee9AF62F66CbE0d9B',
})
)
setLoading(false)
}
if (tokens.length > 0) {
setLoading(false)
}
})()
}, [account.address])
useEffect(() => {
setFilteredNftData(tokens)
}, [tokens])
useEffect(() => {
const firstPageIndex = (currentPage - 1) * limit
const lastPageIndex = firstPageIndex + limit
setPageData(filteredNftData?.slice(firstPageIndex, lastPageIndex))
}, [filteredNftData, currentPage])
useEffect(() => {
setFilteredNftData(
tokens?.filter(
collection =>
(collection.creatorName?.toLowerCase()?.includes(searchValue) ||
collection.name?.toLowerCase()?.includes(searchValue) ||
collection.symbol?.toLowerCase()?.includes(searchValue)) &&
(selectedFilters?.length === filterData.length
? collection.type
: selectedFilters.includes(collection.type))
)
)
}, [searchValue, selectedFilters])
const learnMoreContent = [
{
id: 1,
question: t('learnMoreContent-question-1'),
answer: t('learnMoreContent-answer-1'),
},
{
id: 2,
question: t('learnMoreContent-question-2'),
answer: t('learnMoreContent-answer-2'),
},
{
id: 3,
question: t('learnMoreContent-question-3'),
answer: t('learnMoreContent-answer-3'),
},
{
id: 4,
question: t('learnMoreContent-question-4'),
answer: t('learnMoreContent-answer-4'),
},
]
const tableColumn = [
{
title: t('event'),
dataIndex: 'event',
key: 'event',
},
{
title: t('price'),
dataIndex: 'price',
key: 'price',
},
{
title: t('from'),
dataIndex: 'from',
key: 'from',
},
{
title: t('to'),
dataIndex: 'to',
key: 'to',
// render: () => <a href="#">Delete</a>,
},
{
title: t('date'),
dataIndex: 'date',
key: 'date',
},
]
const tableData = [
{
id: 1,
event: t('minted'),
price: '0.1 ETH',
from: 'Null Address',
to: 'E36370',
date: '03/23/22',
},
{
id: 2,
event: t('minted'),
price: '0.1 ETH',
from: 'Null Address',
to: 'E36370',
date: '03/23/22',
},
{
id: 3,
event: t('minted'),
price: '0.1 ETH',
from: 'Null Address',
to: 'E36370',
date: '03/23/22',
},
{
id: 4,
event: t('minted'),
price: '0.1 ETH',
from: 'Null Address',
to: 'E36370',
date: '03/23/22',
},
{
id: 5,
event: t('minted'),
price: '0.1 ETH',
from: 'Null Address',
to: 'E36370',
date: '03/23/22',
},
]
const sortOptions = [
{
title: t('collection-name'),
active: 'name',
},
{
title: t('creator-name'),
active: 'creatorName',
},
{
title: t('symbol'),
active: 'symbol',
},
]
useEffect(() => {
dispatch(getNFTs({ page: currentPage, limit, search: searchValue }))
dispatch(getUserDetails({ appType: 'chalk' }))
}, [])
const darkMode = useDarkMode()
const handleFilters = value => {
if (value === 'all') {
selectedFilters?.length === filterData?.length
? setSelectedFilters([])
: setSelectedFilters([
'all',
'purchased',
'Owner Mint',
'minted',
'draft',
'Packs',
])
return
}
setSelectedFilters(prev =>
selectedFilters?.includes(value)
? prev.filter(item => item !== value && item !== 'all')
: [...prev, value]
)
}
const onClickSortOption = item => {
setFilteredNftData(
filteredNftData?.sort(function (a, b) {
let x = a[item.active]?.toLowerCase()
let y = b[item.active]?.toLowerCase()
if (x < y) {
return -1
}
if (x > y) {
return 1
}
return 0
})
)
setSelectedSort(item)
}
useOnClickOutside({ ref: dropDownRef, onClick: () => setIsActive(false) })
const dropDownhandler = () => {
setIsActive(!isActive)
}
useEffect(() => {
if (editUserName && userNameRef.current) {
userNameRef.current.focus()
}
if (editBiography && biographyRef.current) {
biographyRef.current.focus()
}
}, [editBiography, editUserName])
const updateUserInfo = () => {
setMessage(t('profile:saving-user-info'))
userImages?.avatar &&
dispatch(
saveUserInfo({
appType: 'chalk',
avatar: userImages?.avatar,
setMessage,
})
)
userImages?.bg_img &&
dispatch(
saveUserInfo({
appType: 'chalk',
bg_img: userImages.bg_img,
setMessage,
})
)
userData?.biography !== userDetails?.biography &&
dispatch(
saveUserInfo({
appType: 'chalk',
biography: userData.biography,
setMessage,
})
)
userData?.userName !== userDetails?.user_name &&
dispatch(
saveUserInfo({
appType: 'chalk',
userName: userData.userName,
setMessage,
})
)
setTimeout(() => {
setMessage('')
setEditMode(false)
dispatch(getUserDetails({ appType: 'chalk' }))
}, [2000])
}
return (
<MainContainer>
<div className={clsx(stl.container, !darkMode.value && stl.lightMode)}>
<BreadCrumbs path={path} />
<div className={stl.mediaContainer}>
<div className={stl.profilePictureHolder}>
<div
className={stl.profilePicture}
style={{
backgroundImage: `url(${
userImages?.bg_img_url
? userImages?.bg_img_url
: userData?.bg_img
? `data:image/png;base64,${userData?.bg_img}`
: profilePicture
})`,
}}
/>
{editMode && (
<div className={stl.backgroundPictureEditbtn}>
<input
type="file"
hidden
id="bgimg-input"
className={stl.avatarInput}
onChange={event =>
setUserImages(prev => ({
...prev,
bg_img: event.target.files[0],
bg_img_url: URL.createObjectURL(event.target.files[0]),
}))
}
/>
<ActionButton
variant="default"
OneIcon={EditIcon}
onClick={() => document.getElementById('bgimg-input').click()}
/>
</div>
)}
</div>
<div className={stl.cardProfile}>
<div>
<input
type="file"
hidden
id="avatar-input"
className={stl.avatarInput}
onChange={event =>
setUserImages(prev => ({
...prev,
avatar: event.target.files[0],
}))
}
/>
<img
src={
userImages?.avatar
? URL.createObjectURL(userImages?.avatar)
: userData?.avatar
? `data:image/png;base64,${userData?.avatar}`
: profilePicture
}
alt="ts"
height={290}
width={220}
/>
{editMode && (
<div className={stl.profilePicureEditbtn}>
<ActionButton
variant="default"
OneIcon={EditIcon}
onClick={() =>
document.getElementById('avatar-input').click()
}
/>
</div>
)}
</div>
<div className={stl.optionsContainer}>
{editMode || (
<ActionButton
variant="default"
OneIcon={EditIcon}
onClick={() => setEditMode(true)}
/>
)}
<ActionButton variant="default" OneIcon={Refresh} />
<ActionButton variant="default" OneIcon={Share} />
<ActionButton variant="default" OneIcon={Link} />
<div className={stl.dropDown} ref={dropDownRef}>
<ActionButton
onClick={dropDownhandler}
OneIcon={MoreOptions}
variant="default"
/>
{isActive && (
<div className={stl.moreOptionsMenu}>
<ul>
<li>
<button>{t('do-something')}</button>
</li>
</ul>
</div>
)}
</div>
</div>
</div>
<div className={stl.detailFormContainer}>
<div
className={
editMode
? stl.profileNameEditableContainer
: stl.profileNameContainer
}
>
{editMode && !editUserName && (
<div
className={stl.profileNameEditPen}
onClick={() => {
setEditUserName(true)
}}
>
<EditIcon />
</div>
)}
<div className={stl.userNameContaier}>
<div className={stl.userIcon}>
<UserIcon />
</div>
<input
type="text"
placeHolder={t('profile:enter-username')}
className={stl.userNameText}
value={userData?.userName}
ref={userNameRef}
onChange={event =>
setUserData(prev => ({
...prev,
userName: event.target.value,
}))
}
readOnly={!editUserName}
onBlur={() => {
setEditUserName(false)
}}
/>
</div>
</div>
</div>
<p className={stl.joinedText}>
{t('profile:joined')}{' '}
{new Date(userDetails?.created_at)?.toLocaleString('default', {
month: 'long',
})}{' '}
{new Date(userDetails?.created_at)?.getFullYear()}
</p>
<div
className={
editMode ? stl.biographyEditableContainer : stl.biographyContainer
}
>
{editMode && !editBiography && (
<div
className={stl.profileNameEditPen}
onClick={() => {
setEditBiography(true)
}}
>
<EditIcon />
</div>
)}
<input
type="text"
placeHolder={t('profile:add-a-biography')}
className={stl.biographyText}
value={userData?.biography}
ref={biographyRef}
onChange={event =>
setUserData(prev => ({
...prev,
biography: event.target.value,
}))
}
readOnly={!editBiography}
onBlur={() => {
setEditBiography(false)
}}
/>
</div>
<TitleBox
customClass={clsx(stl.addressBox, stl.titleBox)}
enableCopied={true}
content={account?.address}
leftIcon={<EthIcon />}
/>
<TitleBox
customClass={clsx(stl.addressBox, stl.titleBox)}
enableCopied={true}
content={activeWallet?.id}
leftIcon={<ZCNIcon />}
/>
<div className={stl.nftSearchContainer}>
<TitleBox
customClass={stl.titleBox}
content={`112 ${t('purchased')} NFTs`}
leftIcon={<ActiveSaleIcon />}
/>
<TitleBox
customClass={stl.titleBox}
content={`28 ${t('minted')} NFTs`}
leftIcon={<PublicMintIcon />}
/>
<TitleBox
customClass={stl.titleBox}
content={`111 ${t('packs')}`}
leftIcon={<PackMintIcon />}
/>
</div>
{editMode && (
<div className={stl.actionBtnsContainer}>
<ActionButton
variant="darkGrey"
customClass={stl.actionButton}
onClick={() => {
setEditMode(false)
setUserData(prev => ({
...prev,
userName: userDetails?.user_name,
biography: userDetails?.biography,
}))
setUserImages({})
}}
>
{t('cancel')}
</ActionButton>
<ActionButton
variant="white"
customClass={stl.actionButton}
disabled={message.length > 0}
onClick={updateUserInfo}
>
{t('profile:save-profile-changes')}
</ActionButton>
</div>
)}
<div>{message}</div>
<div className={stl.collectionSection}>
<p className={stl.collectionText}>{t('collections')}</p>
</div>
<div className={stl.collectionContainer}>
<div className={stl.filterContainer}>
<div className={stl.filterBox}>
<div style={{ position: 'relative' }}>
<FilterDropdown
id="filters"
placeHolder={t('filters')}
Icon={CubeIcon}
options={filterData}
onClick={value => handleFilters(value)}
selectedOption={selectedFilters}
customClass={stl.multiSelectDropdown}
isMultiSelect
/>
</div>
<FilterDropdown
id="sort-by-name"
placeHolder={t('sort-by-name')}
options={sortOptions}
onClick={onClickSortOption}
selectedOption={selectedSort}
customClass={stl.dropdown}
/>
<SearchBar
value={searchValue}
onChange={value => setSearchValue(value?.toLowerCase())}
placeHolder={t('search-for-name-creator')}
suggestionOptions={[
'these',
'are',
'sample',
'search',
'suggestion',
]}
customClass={stl.searchBar}
/>
</div>
</div>
<div className={stl.nftContainerBox}>
<div className={stl.wrapper}>
{loading ? (
<LoadingBox />
) : pageData.length <= 0 ? (
<h2>{t('data-not-found')}</h2>
) : (
pageData?.map((eachData, index) => {
return (
<NftBox
key={index}
{...eachData}
customClass={stl.nftBoxContainer}
/>
)
})
)}
</div>
</div>
<Pagination
totalCount={filteredNftData?.length}
currentPage={currentPage}
onChange={setCurrentPage}
customClass={stl.paginationContainer}
pageSize={limit}
/>
</div>
<div className={stl.userActivityContainer}>
<p className={stl.userActivityText}>{t('user-activity')}</p>
</div>
<div className={stl.tableContainer}>
<ActivityTable
columns={tableColumn}
data={tableData}
id={'table-id'}
footerText={t('activity-tab-footer')}
/>
</div>
</div>
</div>
<div
className={clsx(
!darkMode.value && stl.learnMoreLightMode,
stl.learnMoreMainContainer
)}
>
<LearnMore
heading={t('learnMore-heading')}
description={t('learnMore-description')}
content={learnMoreContent}
/>
</div>
</MainContainer>
)
}
export default Profile
import sha3 from 'js-sha3'
import { hexStringToByte, getBls } from 'lib/utils'
export async function getTxnSignature(asset, privateKey) {
const hexHash = sha3.sha3_256(asset)
const bytehash = hexStringToByte(hexHash)
const bls = await getBls()
const sec = new bls.SecretKey()
sec.deserializeHexStr(privateKey)
const sig = sec.sign(bytehash)
return sig.serializeToHexStr()
}
function getTxnHash(props) {
const { txnData, ts, walletId, recipientId, nonce, txnValue = 0 } = props
const hashPayload = sha3.sha3_256(txnData)
const hashData = `${ts}:${nonce}:${walletId}:${recipientId}:${txnValue}:${hashPayload}`
const hash = sha3.sha3_256(hashData)
return { hashData, hash }
}
export const getTxnPayload = async ({
wallet,
recipientId,
txnValue,
txnData,
txnType,
nonce,
markerTs,
}) => {
const { keys, id } = wallet
let ts = Math.floor(new Date().getTime() / 1000)
// Later implement getTimestamp(), as in js-client-sdk
// hack to get the timestamp verification working correctly on the backend
if (markerTs && markerTs >= ts) {
ts = markerTs + 1
}
const { hashData, hash } = await getTxnHash({
txnData,
ts,
walletId: id,
recipientId,
nonce,
txnValue,
})
const signature = await getTxnSignature(hashData, keys.privateKey)
const txnPayload = {
client_id: id,
public_key: keys.publicKey,
to_client_id: recipientId,
transaction_value: txnValue,
transaction_data: txnData,
transaction_type: txnType,
transaction_nonce: nonce,
transaction_fee: 0,
txn_output_hash: '',
creation_date: ts,
version: '1.0',
signature,
hash,
}
return txnPayload
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment