Created
February 14, 2023 01:30
-
-
Save Rizary/779568d03165234c8bb7680b76f4f3c7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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 | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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 | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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