feat: UI integrate ens dropdown (#143)
* chore: get ens names from address * wip: ens validation * wip: combobox with option to add new items * chore: add trim words * chore: change order steps * chore: add comments * chore: change components test view * chore: remove unused file * chore: add alchemy-sdk as prod dependency * chore: pr comments
This commit is contained in:
parent
8e309ee04c
commit
e5d28251c4
|
|
@ -1,4 +1,11 @@
|
|||
import { Address, Bytes, log, store, ethereum, BigInt } from '@graphprotocol/graph-ts';
|
||||
import {
|
||||
Address,
|
||||
Bytes,
|
||||
log,
|
||||
store,
|
||||
ethereum,
|
||||
BigInt,
|
||||
} from '@graphprotocol/graph-ts';
|
||||
|
||||
// Event Imports [based on the yaml config]
|
||||
import {
|
||||
|
|
@ -35,11 +42,11 @@ import {
|
|||
|
||||
enum CollectionRoles {
|
||||
Owner,
|
||||
};
|
||||
}
|
||||
|
||||
enum TokenRoles {
|
||||
Controller,
|
||||
};
|
||||
}
|
||||
|
||||
export function handleApproval(event: ApprovalEvent): void {
|
||||
let entity = new Approval(
|
||||
|
|
@ -263,27 +270,29 @@ export function handleMetadataUpdateWithIntValue(
|
|||
}
|
||||
|
||||
export function handleInitialized(event: InitializedEvent): void {
|
||||
// This is the contract creation transaction.
|
||||
log.warning('This is the contract creation transaction.', []);
|
||||
if (event.receipt) {
|
||||
let receipt = event.receipt as ethereum.TransactionReceipt;
|
||||
log.warning('Contract address is: {}', [
|
||||
receipt.contractAddress.toHexString(),
|
||||
]);
|
||||
|
||||
// add owner
|
||||
let owner = new Owner(event.transaction.from);
|
||||
owner.collection = true;
|
||||
owner.save();
|
||||
}
|
||||
// This is the contract creation transaction.
|
||||
log.warning('This is the contract creation transaction.', []);
|
||||
if (event.receipt) {
|
||||
let receipt = event.receipt as ethereum.TransactionReceipt;
|
||||
log.warning('Contract address is: {}', [
|
||||
receipt.contractAddress.toHexString(),
|
||||
]);
|
||||
|
||||
// add owner
|
||||
let owner = new Owner(event.transaction.from);
|
||||
owner.collection = true;
|
||||
owner.save();
|
||||
}
|
||||
}
|
||||
|
||||
export function handleCollectionRoleChanged(event: CollectionRoleChangedEvent): void {
|
||||
export function handleCollectionRoleChanged(
|
||||
event: CollectionRoleChangedEvent
|
||||
): void {
|
||||
let toAddress = event.params.toAddress;
|
||||
let byAddress = event.params.byAddress;
|
||||
let role = event.params.role;
|
||||
let status = event.params.status;
|
||||
|
||||
|
||||
if (role === CollectionRoles.Owner) {
|
||||
// Owner role
|
||||
if (status) {
|
||||
|
|
@ -298,14 +307,21 @@ export function handleCollectionRoleChanged(event: CollectionRoleChangedEvent):
|
|||
// revoked
|
||||
let owner = Owner.load(toAddress);
|
||||
if (!owner) {
|
||||
log.error('Owner entity not found. Role: {}, byAddress: {}, toAddress: {}', [role.toString(), byAddress.toHexString(), toAddress.toHexString()]);
|
||||
log.error(
|
||||
'Owner entity not found. Role: {}, byAddress: {}, toAddress: {}',
|
||||
[role.toString(), byAddress.toHexString(), toAddress.toHexString()]
|
||||
);
|
||||
return;
|
||||
}
|
||||
owner.collection = false;
|
||||
owner.save();
|
||||
}
|
||||
} else {
|
||||
log.error('Role not supported. Role: {}, byAddress: {}, toAddress: {}', [role.toString(), byAddress.toHexString(), toAddress.toHexString()]);
|
||||
log.error('Role not supported. Role: {}, byAddress: {}, toAddress: {}', [
|
||||
role.toString(),
|
||||
byAddress.toHexString(),
|
||||
toAddress.toHexString(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -315,13 +331,13 @@ export function handleTokenRoleChanged(event: TokenRoleChangedEvent): void {
|
|||
let byAddress = event.params.byAddress;
|
||||
let role = event.params.role;
|
||||
let status = event.params.status;
|
||||
|
||||
|
||||
// load token
|
||||
let token = Token.load(Bytes.fromByteArray(Bytes.fromBigInt(tokenId)));
|
||||
if (!token) {
|
||||
log.error('Token not found. TokenId: {}', [tokenId.toString()]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (role === TokenRoles.Controller) {
|
||||
// Controller role
|
||||
|
|
@ -343,11 +359,17 @@ export function handleTokenRoleChanged(event: TokenRoleChangedEvent): void {
|
|||
}
|
||||
token.controllers = token_controllers;
|
||||
} else {
|
||||
log.error('Role not supported. Role: {}, byAddress: {}, toAddress: {}', [role.toString(), byAddress.toHexString(), toAddress.toHexString()]);
|
||||
log.error('Role not supported. Role: {}, byAddress: {}, toAddress: {}', [
|
||||
role.toString(),
|
||||
byAddress.toHexString(),
|
||||
toAddress.toHexString(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
export function handleMetadataUpdateWithBooleanValue(event: MetadataUpdateEvent3): void {
|
||||
export function handleMetadataUpdateWithBooleanValue(
|
||||
event: MetadataUpdateEvent3
|
||||
): void {
|
||||
/**
|
||||
* accessPointAutoApproval
|
||||
*/
|
||||
|
|
@ -364,7 +386,9 @@ export function handleMetadataUpdateWithBooleanValue(event: MetadataUpdateEvent3
|
|||
|
||||
entity.save();
|
||||
|
||||
let token = Token.load(Bytes.fromByteArray(Bytes.fromBigInt(event.params._tokenId)));
|
||||
let token = Token.load(
|
||||
Bytes.fromByteArray(Bytes.fromBigInt(event.params._tokenId))
|
||||
);
|
||||
|
||||
if (token) {
|
||||
if (event.params.key == 'accessPointAutoApproval') {
|
||||
|
|
@ -420,14 +444,13 @@ export function handleTransfer(event: TransferEvent): void {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This handler will create and load entities in the following order:
|
||||
* - AccessPoint [create]
|
||||
* - Owner [load / create]
|
||||
* Note to discuss later: Should a `NewAccessPoint` entity be also created and defined?
|
||||
*/
|
||||
export function handleNewAccessPoint(event: NewAccessPointEvent): void {
|
||||
* This handler will create and load entities in the following order:
|
||||
* - AccessPoint [create]
|
||||
* - Owner [load / create]
|
||||
* Note to discuss later: Should a `NewAccessPoint` entity be also created and defined?
|
||||
*/
|
||||
export function handleNewAccessPoint(event: NewAccessPointEvent): void {
|
||||
// Create an AccessPoint entity
|
||||
let accessPointEntity = new AccessPoint(event.params.apName);
|
||||
accessPointEntity.score = BigInt.fromU32(0);
|
||||
|
|
@ -435,7 +458,9 @@ export function handleTransfer(event: TransferEvent): void {
|
|||
accessPointEntity.nameVerified = false;
|
||||
accessPointEntity.creationStatus = 'DRAFT'; // Since a `ChangeAccessPointCreationStatus` event is emitted instantly after `NewAccessPoint`, the status will be updated in that handler.
|
||||
accessPointEntity.owner = event.params.owner;
|
||||
accessPointEntity.token = Bytes.fromByteArray(Bytes.fromBigInt(event.params.tokenId));
|
||||
accessPointEntity.token = Bytes.fromByteArray(
|
||||
Bytes.fromBigInt(event.params.tokenId)
|
||||
);
|
||||
|
||||
// Load / Create an Owner entity
|
||||
let ownerEntity = Owner.load(event.params.owner);
|
||||
|
|
@ -453,7 +478,9 @@ export function handleTransfer(event: TransferEvent): void {
|
|||
/**
|
||||
* This handler will update the status of an access point entity.
|
||||
*/
|
||||
export function handleChangeAccessPointCreationStatus(event: ChangeAccessPointCreationStatusEvent): void {
|
||||
export function handleChangeAccessPointCreationStatus(
|
||||
event: ChangeAccessPointCreationStatusEvent
|
||||
): void {
|
||||
// Load the AccessPoint entity
|
||||
let accessPointEntity = AccessPoint.load(event.params.apName);
|
||||
let status = event.params.status;
|
||||
|
|
@ -474,20 +501,28 @@ export function handleChangeAccessPointCreationStatus(event: ChangeAccessPointCr
|
|||
break;
|
||||
default:
|
||||
// Unknown status
|
||||
log.error('Unable to handle ChangeAccessPointCreationStatus. Unknown status. Status: {}, AccessPoint: {}', [status.toString(), event.params.apName]);
|
||||
log.error(
|
||||
'Unable to handle ChangeAccessPointCreationStatus. Unknown status. Status: {}, AccessPoint: {}',
|
||||
[status.toString(), event.params.apName]
|
||||
);
|
||||
}
|
||||
|
||||
accessPointEntity.save();
|
||||
} else {
|
||||
// Unknown access point
|
||||
log.error('Unable to handle ChangeAccessPointCreationStatus. Unknown access point. Status: {}, AccessPoint: {}', [status.toString(), event.params.apName]);
|
||||
log.error(
|
||||
'Unable to handle ChangeAccessPointCreationStatus. Unknown access point. Status: {}, AccessPoint: {}',
|
||||
[status.toString(), event.params.apName]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This handler will update the score of an access point entity.
|
||||
*/
|
||||
export function handleChangeAccessPointScore(event: ChangeAccessPointCreationScoreEvent): void {
|
||||
export function handleChangeAccessPointScore(
|
||||
event: ChangeAccessPointCreationScoreEvent
|
||||
): void {
|
||||
// Load the AccessPoint entity
|
||||
let accessPointEntity = AccessPoint.load(event.params.apName);
|
||||
|
||||
|
|
@ -496,14 +531,19 @@ export function handleChangeAccessPointCreationStatus(event: ChangeAccessPointCr
|
|||
accessPointEntity.save();
|
||||
} else {
|
||||
// Unknown access point
|
||||
log.error('Unable to handle ChangeAccessPointScore. Unknown access point. Score: {}, AccessPoint: {}', [event.params.score.toString(), event.params.apName]);
|
||||
log.error(
|
||||
'Unable to handle ChangeAccessPointScore. Unknown access point. Score: {}, AccessPoint: {}',
|
||||
[event.params.score.toString(), event.params.apName]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This handler will update the nameVerified field of an access point entity.
|
||||
*/
|
||||
export function handleChangeAccessPointNameVerify(event: ChangeAccessPointNameVerifyEvent): void {
|
||||
export function handleChangeAccessPointNameVerify(
|
||||
event: ChangeAccessPointNameVerifyEvent
|
||||
): void {
|
||||
// Load the AccessPoint entity
|
||||
let accessPointEntity = AccessPoint.load(event.params.apName);
|
||||
|
||||
|
|
@ -512,14 +552,19 @@ export function handleChangeAccessPointCreationStatus(event: ChangeAccessPointCr
|
|||
accessPointEntity.save();
|
||||
} else {
|
||||
// Unknown access point
|
||||
log.error('Unable to handle ChangeAccessPointNameVerify. Unknown access point. Verified: {}, AccessPoint: {}', [event.params.verified.toString(), event.params.apName]);
|
||||
log.error(
|
||||
'Unable to handle ChangeAccessPointNameVerify. Unknown access point. Verified: {}, AccessPoint: {}',
|
||||
[event.params.verified.toString(), event.params.apName]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This handler will update the contentVerified field of an access point entity.
|
||||
*/
|
||||
export function handleChangeAccessPointContentVerify(event: ChangeAccessPointContentVerifyEvent): void {
|
||||
export function handleChangeAccessPointContentVerify(
|
||||
event: ChangeAccessPointContentVerifyEvent
|
||||
): void {
|
||||
// Load the AccessPoint entity
|
||||
let accessPointEntity = AccessPoint.load(event.params.apName);
|
||||
|
||||
|
|
@ -528,6 +573,9 @@ export function handleChangeAccessPointCreationStatus(event: ChangeAccessPointCr
|
|||
accessPointEntity.save();
|
||||
} else {
|
||||
// Unknown access point
|
||||
log.error('Unable to handle ChangeAccessPointContentVerify. Unknown access point. Verified: {}, AccessPoint: {}', [event.params.verified.toString(), event.params.apName]);
|
||||
log.error(
|
||||
'Unable to handle ChangeAccessPointContentVerify. Unknown access point. Verified: {}, AccessPoint: {}',
|
||||
[event.params.verified.toString(), event.params.apName]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
"@reduxjs/toolkit": "^1.9.1",
|
||||
"@stitches/react": "^1.2.8",
|
||||
"abitype": "^0.5.0",
|
||||
"alchemy-sdk": "^2.5.0",
|
||||
"colorthief": "^2.3.2",
|
||||
"connectkit": "^1.1.3",
|
||||
"firebase": "^9.17.1",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { BrowserRouter, Route, Routes, Navigate } from 'react-router-dom';
|
||||
import { themeGlobals } from '@/theme/globals';
|
||||
import { Home, Mint } from './views';
|
||||
import { ComponentsTest, Home, Mint } from './views';
|
||||
import { SVGTestScreen } from './views/svg-test'; // TODO: remove when done
|
||||
import { ConnectKitButton } from 'connectkit';
|
||||
import { MintTest } from './views/mint-test';
|
||||
|
|
@ -18,6 +18,8 @@ export const App = () => {
|
|||
<Route path="/home" element={<Home />} />
|
||||
<Route path="/mint" element={<Mint />} />
|
||||
<Route path="/svg" element={<SVGTestScreen />} />
|
||||
{/** TODO remove for release */}
|
||||
<Route path="/components-test" element={<ComponentsTest />} />
|
||||
<Route path="/mint-test" element={<MintTest />} />
|
||||
<Route path="*" element={<Navigate to="/home" />} />
|
||||
</Routes>
|
||||
|
|
|
|||
|
|
@ -1,39 +1,60 @@
|
|||
import React, { Fragment, useRef, useState } from 'react';
|
||||
import React, { Fragment, useEffect, useRef, useState } from 'react';
|
||||
import { Combobox as ComboboxLib, Transition } from '@headlessui/react';
|
||||
import { Icon } from '@/components/core/icon';
|
||||
import { Icon, IconName } from '@/components/core/icon';
|
||||
import { Flex } from '@/components/layout';
|
||||
import { useDebounce } from '@/hooks/use-debounce';
|
||||
import { Separator } from '../separator.styles';
|
||||
import { cleanString } from './combobox.utils';
|
||||
|
||||
type ComboboxInputProps = {
|
||||
/**
|
||||
* If it's true, the list of options will be displayed
|
||||
*/
|
||||
open: boolean;
|
||||
/**
|
||||
* Name of the left icon to display in the input
|
||||
*/
|
||||
leftIcon: IconName;
|
||||
/**
|
||||
* Function to handle the input change
|
||||
*/
|
||||
handleInputChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
/**
|
||||
* Function to handle the input click. When the user clicks on the input, the list of options will be displayed
|
||||
*/
|
||||
handleInputClick: () => void;
|
||||
};
|
||||
|
||||
const ComboboxInput = ({
|
||||
open,
|
||||
leftIcon,
|
||||
handleInputChange,
|
||||
handleInputClick,
|
||||
}: ComboboxInputProps) => (
|
||||
<div className="relative w-full cursor-default ">
|
||||
<Icon
|
||||
name="search"
|
||||
name={leftIcon}
|
||||
size="sm"
|
||||
css={{
|
||||
position: 'absolute',
|
||||
left: '$3',
|
||||
top: '0.9375rem',
|
||||
top: '$3',
|
||||
fontSize: '$xl',
|
||||
color: 'slate8',
|
||||
}}
|
||||
/>
|
||||
<ComboboxLib.Input
|
||||
placeholder="Search"
|
||||
className={`w-full border-solid border border-slate7 h-11 py-3 pl-8 pr-10 text-sm bg-transparent leading-5 text-slate11 outline-none ${
|
||||
className={`w-full border-solid border border-slate7 h-11 py-3 pl-10 pr-10 text-sm bg-transparent leading-5 text-slate11 outline-none ${
|
||||
open ? 'border-b-0 rounded-t-xl bg-black border-slate6' : 'rounded-xl'
|
||||
}`}
|
||||
displayValue={(selectedValue: ComboboxItem) => selectedValue.label}
|
||||
onChange={handleInputChange}
|
||||
onClick={handleInputClick}
|
||||
/>
|
||||
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-4">
|
||||
<Icon name="chevron-down" css={{ fontSize: '$xs' }} />
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
|
|
@ -52,9 +73,13 @@ const ComboboxOption = ({ option }: ComboboxOptionProps) => (
|
|||
>
|
||||
{({ selected, active }) => (
|
||||
<Flex css={{ justifyContent: 'space-between' }}>
|
||||
<Flex css={{ flexDirection: 'row' }}>
|
||||
<Flex css={{ flexDirection: 'row', maxWidth: '95%' }}>
|
||||
{option.icon}
|
||||
<span className={`${active ? 'text-slate12' : 'text-slate11'}`}>
|
||||
<span
|
||||
className={`${active ? 'text-slate12' : 'text-slate11'} ${
|
||||
option.icon ? 'max-w-70' : 'max-w-full'
|
||||
} whitespace-nowrap text-ellipsis overflow-hidden`}
|
||||
>
|
||||
{option.label}
|
||||
</span>
|
||||
</Flex>
|
||||
|
|
@ -73,38 +98,97 @@ export const NoResults = ({ css }: { css?: string }) => (
|
|||
);
|
||||
|
||||
export type ComboboxItem = {
|
||||
/**
|
||||
* The key of the item.
|
||||
*/
|
||||
value: string;
|
||||
/**
|
||||
* The label to display of the item.
|
||||
*/
|
||||
label: string;
|
||||
/**
|
||||
* Optional icon to display on the left of the item.
|
||||
*/
|
||||
icon?: React.ReactNode;
|
||||
};
|
||||
|
||||
export type ComboboxProps = {
|
||||
/**
|
||||
* List of items to be displayed in the combobox.
|
||||
*/
|
||||
items: ComboboxItem[];
|
||||
/**
|
||||
* The selected value of the combobox.
|
||||
*/
|
||||
selectedValue: ComboboxItem | undefined;
|
||||
/**
|
||||
* If true, the combobox will add the input if it doesn't exist in the list of items.
|
||||
*/
|
||||
withAutocomplete?: boolean;
|
||||
/**
|
||||
* Name of the left icon to display in the input. Defualt is "search".
|
||||
*/
|
||||
leftIcon?: IconName;
|
||||
/**
|
||||
* Callback when the selected value changes.
|
||||
*/
|
||||
onChange(option: ComboboxItem): void;
|
||||
};
|
||||
|
||||
export const Combobox: React.FC<ComboboxProps> = ({
|
||||
items,
|
||||
selectedValue = { value: '', label: '' },
|
||||
withAutocomplete = false,
|
||||
leftIcon = 'search',
|
||||
onChange,
|
||||
}) => {
|
||||
const [query, setQuery] = useState('');
|
||||
const [filteredItems, setFilteredItems] = useState<ComboboxItem[]>([]);
|
||||
const [autocompleteItems, setAutocompleteItems] = useState<ComboboxItem[]>(
|
||||
[]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// If the selected value doesn't exist in the list of items, we add it
|
||||
if (
|
||||
items.filter((item) => item === selectedValue).length === 0 &&
|
||||
selectedValue.value !== undefined &&
|
||||
autocompleteItems.length === 0 &&
|
||||
withAutocomplete
|
||||
) {
|
||||
setAutocompleteItems([selectedValue]);
|
||||
}
|
||||
}, [selectedValue]);
|
||||
|
||||
useEffect(() => {
|
||||
setFilteredItems(items);
|
||||
}, [items]);
|
||||
|
||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
const filteredItems =
|
||||
query === ''
|
||||
? items
|
||||
: items.filter((person) =>
|
||||
person.label
|
||||
.toLowerCase()
|
||||
.replace(/\s+/g, '')
|
||||
.includes(query.toLowerCase().replace(/\s+/g, ''))
|
||||
);
|
||||
const handleSearch = useDebounce((searchValue: string) => {
|
||||
if (searchValue === '') {
|
||||
setFilteredItems(items);
|
||||
|
||||
if (withAutocomplete) {
|
||||
setAutocompleteItems([]);
|
||||
handleComboboxChange({} as ComboboxItem);
|
||||
}
|
||||
} else {
|
||||
const filteredValues = items.filter((item) =>
|
||||
cleanString(item.label).startsWith(cleanString(searchValue))
|
||||
);
|
||||
|
||||
if (withAutocomplete && filteredValues.length === 0) {
|
||||
// If the search value doesn't exist in the list of items, we add it
|
||||
setAutocompleteItems([{ value: searchValue, label: searchValue }]);
|
||||
}
|
||||
setFilteredItems(filteredValues);
|
||||
}
|
||||
}, 200);
|
||||
|
||||
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setQuery(event.target.value);
|
||||
event.stopPropagation();
|
||||
handleSearch(event.target.value);
|
||||
};
|
||||
|
||||
const handleInputClick = () => {
|
||||
|
|
@ -116,7 +200,11 @@ export const Combobox: React.FC<ComboboxProps> = ({
|
|||
};
|
||||
|
||||
const handleLeaveTransition = () => {
|
||||
setQuery('');
|
||||
setFilteredItems(items);
|
||||
if (selectedValue.value === undefined && withAutocomplete) {
|
||||
setAutocompleteItems([]);
|
||||
handleComboboxChange({} as ComboboxItem);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
@ -131,6 +219,7 @@ export const Combobox: React.FC<ComboboxProps> = ({
|
|||
handleInputChange={handleInputChange}
|
||||
handleInputClick={handleInputClick}
|
||||
open={open}
|
||||
leftIcon={leftIcon}
|
||||
/>
|
||||
<ComboboxLib.Button ref={buttonRef} className="hidden" />
|
||||
|
||||
|
|
@ -144,12 +233,25 @@ export const Combobox: React.FC<ComboboxProps> = ({
|
|||
afterLeave={handleLeaveTransition}
|
||||
>
|
||||
<ComboboxLib.Options className="absolute max-h-60 w-full z-10 overflow-auto rounded-b-xl border-solid border-slate6 border bg-black pt-2 px-3 text-base focus:outline-none sm:text-sm">
|
||||
{filteredItems.length === 0 && query !== '' ? (
|
||||
{[...autocompleteItems, ...filteredItems].length === 0 ||
|
||||
filteredItems === undefined ? (
|
||||
<NoResults />
|
||||
) : (
|
||||
filteredItems.map((option: ComboboxItem) => {
|
||||
return <ComboboxOption key={option.value} option={option} />;
|
||||
})
|
||||
<>
|
||||
{autocompleteItems.length > 0 && <span>Create new</span>}
|
||||
{autocompleteItems.map((autocompleteOption: ComboboxItem) => (
|
||||
<ComboboxOption
|
||||
key={autocompleteOption.value}
|
||||
option={autocompleteOption}
|
||||
/>
|
||||
))}
|
||||
{autocompleteItems.length > 0 && filteredItems.length > 0 && (
|
||||
<Separator css={{ mb: '$2' }} />
|
||||
)}
|
||||
{filteredItems.map((option: ComboboxItem) => (
|
||||
<ComboboxOption key={option.value} option={option} />
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</ComboboxLib.Options>
|
||||
</Transition>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
export const cleanString = (str: string) =>
|
||||
str.toLowerCase().replace(/\s+/g, '');
|
||||
|
|
@ -3,6 +3,10 @@ export const env = Object.freeze({
|
|||
id: import.meta.env.VITE_ALCHEMY_API_KEY || '',
|
||||
appName: import.meta.env.VITE_ALCHEMY_APP_NAME || '',
|
||||
},
|
||||
ens: {
|
||||
contractAddress: import.meta.env.VITE_ENS_ADDRESS || '',
|
||||
validationEnsURL: import.meta.env.VITE_ENS_VALIDATION_URL || '',
|
||||
},
|
||||
firebase: {
|
||||
apiKey: import.meta.env.VITE_FIREBASE_API_KEY || '',
|
||||
authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN || '',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
import { useRef } from 'react';
|
||||
|
||||
export const useDebounce = <A extends any[], F extends (...args: A) => void>(
|
||||
f: F,
|
||||
t = 500
|
||||
) => {
|
||||
const timeOutRef = useRef<NodeJS.Timeout>();
|
||||
|
||||
return (...args: A) => {
|
||||
timeOutRef.current && clearTimeout(timeOutRef.current);
|
||||
timeOutRef.current = setTimeout(() => {
|
||||
f(...args);
|
||||
}, t);
|
||||
};
|
||||
};
|
||||
|
|
@ -1,8 +1,18 @@
|
|||
import { JsonRpcProvider, Networkish } from '@ethersproject/providers';
|
||||
import { ethers } from 'ethers';
|
||||
import * as Contracts from './contracts';
|
||||
import { env } from '@/constants';
|
||||
import { Alchemy, Network } from 'alchemy-sdk';
|
||||
|
||||
const config = {
|
||||
apiKey: env.alchemy.id,
|
||||
network: Network.ETH_MAINNET,
|
||||
};
|
||||
|
||||
const alchemy = new Alchemy(config);
|
||||
|
||||
export const Ethereum: Ethereum.Core = {
|
||||
//TODO remove
|
||||
defaultNetwork: 'https://rpc-mumbai.maticvigil.com', // TODO: make it environment variable
|
||||
|
||||
provider: {
|
||||
|
|
@ -20,6 +30,23 @@ export const Ethereum: Ethereum.Core = {
|
|||
|
||||
return new ethers.Contract(contract.address, contract.abi, provider);
|
||||
},
|
||||
|
||||
async getEnsName(address) {
|
||||
const ensAddresses = await alchemy.nft.getNftsForOwner(address, {
|
||||
contractAddresses: [env.ens.contractAddress],
|
||||
});
|
||||
|
||||
return ensAddresses.ownedNfts.map((nft) => nft.title);
|
||||
},
|
||||
|
||||
//TODO remove if we're not gonna validate ens on the client side
|
||||
async validateEnsName(name) {
|
||||
const provider = new ethers.providers.JsonRpcProvider(
|
||||
env.ens.validationEnsURL
|
||||
);
|
||||
|
||||
return Boolean(await provider.resolveName(name));
|
||||
},
|
||||
};
|
||||
|
||||
export namespace Ethereum {
|
||||
|
|
@ -36,5 +63,9 @@ export namespace Ethereum {
|
|||
contractName: keyof typeof Contracts,
|
||||
providerName?: Providers
|
||||
) => ethers.Contract;
|
||||
|
||||
getEnsName: (address: string) => Promise<string[]>;
|
||||
|
||||
validateEnsName: (name: string) => Promise<boolean>;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
import { Ethereum } from '@/integrations';
|
||||
import { RootState } from '@/store';
|
||||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||
import { ensActions } from '../ens-slice';
|
||||
|
||||
export const fetchEnsNamesThunk = createAsyncThunk<void, `0x${string}`>(
|
||||
'ens/fetchEnsNames',
|
||||
async (address, { dispatch, getState }) => {
|
||||
const { state } = (getState() as RootState).ens;
|
||||
if (state === 'loading') return;
|
||||
|
||||
try {
|
||||
dispatch(ensActions.setState('loading'));
|
||||
|
||||
//fetch ens names for received addresses
|
||||
const ensList = await Ethereum.getEnsName(address);
|
||||
|
||||
dispatch(ensActions.setEnsNames(ensList));
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
dispatch(ensActions.setState('failed'));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
@ -0,0 +1 @@
|
|||
export * from './fetch-ens-names';
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
import { RootState, useAppSelector } from '@/store';
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import * as asyncThunk from './async-thunk';
|
||||
|
||||
export namespace EnsState {
|
||||
export type State = 'idle' | 'loading' | 'success' | 'failed';
|
||||
|
||||
export type EnsNames = string[];
|
||||
}
|
||||
|
||||
export interface EnsState {
|
||||
state: EnsState.State;
|
||||
ensNames: EnsState.EnsNames;
|
||||
}
|
||||
|
||||
const initialState: EnsState = {
|
||||
state: 'idle',
|
||||
ensNames: [],
|
||||
};
|
||||
|
||||
export const ensSlice = createSlice({
|
||||
name: 'ens',
|
||||
initialState,
|
||||
reducers: {
|
||||
setEnsNames: (state, action: PayloadAction<EnsState.EnsNames>) => {
|
||||
state.ensNames = action.payload;
|
||||
state.state = 'success';
|
||||
},
|
||||
setState: (
|
||||
state,
|
||||
action: PayloadAction<Exclude<EnsState.State, 'success'>>
|
||||
) => {
|
||||
state.state = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const ensActions = {
|
||||
...ensSlice.actions,
|
||||
...asyncThunk,
|
||||
};
|
||||
|
||||
const selectEnsState = (state: RootState): EnsState => state.ens;
|
||||
|
||||
export const useEnsStore = (): EnsState => useAppSelector(selectEnsState);
|
||||
|
||||
export default ensSlice.reducer;
|
||||
|
|
@ -0,0 +1 @@
|
|||
export * from './ens-slice';
|
||||
|
|
@ -16,8 +16,6 @@ export const fetchRepositoriesThunk = createAsyncThunk(
|
|||
|
||||
const repositories = await githubClient.fetchRepos(url);
|
||||
|
||||
console.log(repositories);
|
||||
|
||||
dispatch(
|
||||
githubActions.setRepositories(
|
||||
repositories.map((repo: any) => ({
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
export * from './github';
|
||||
export * from './ens';
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import { configureStore } from '@reduxjs/toolkit';
|
||||
import githubReducer from './features/github/github-slice';
|
||||
import ensReducer from './features/ens/ens-slice';
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
github: githubReducer,
|
||||
ens: ensReducer,
|
||||
},
|
||||
middleware: (getDefaultMiddleware) =>
|
||||
getDefaultMiddleware({
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
import { Combobox, ComboboxItem, Flex } from '@/components';
|
||||
import { useState } from 'react';
|
||||
|
||||
const itemsCombobox = [
|
||||
{ label: 'Item 1', value: 'item-1' },
|
||||
{ label: 'Item 2', value: 'item-2' },
|
||||
{ label: 'Item 3', value: 'item-3' },
|
||||
];
|
||||
|
||||
export const ComponentsTest = () => {
|
||||
const [selectedValue, setSelectedValue] = useState({} as ComboboxItem);
|
||||
const [selectedValueAutocomplete, setSelectedValueAutocomplete] = useState(
|
||||
{} as ComboboxItem
|
||||
);
|
||||
|
||||
const handleComboboxChange = (item: ComboboxItem) => {
|
||||
setSelectedValue(item);
|
||||
};
|
||||
|
||||
const handleComboboxChangeAutocomplete = (item: ComboboxItem) => {
|
||||
setSelectedValueAutocomplete(item);
|
||||
};
|
||||
|
||||
return (
|
||||
<Flex
|
||||
css={{
|
||||
flexDirection: 'column',
|
||||
margin: '100px',
|
||||
|
||||
justifyContent: 'center',
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
<h1>Components Test</h1>
|
||||
<Flex css={{ width: '400px', gap: '$2' }}>
|
||||
<Combobox
|
||||
items={itemsCombobox}
|
||||
selectedValue={selectedValue}
|
||||
onChange={handleComboboxChange}
|
||||
/>
|
||||
<Combobox
|
||||
items={itemsCombobox}
|
||||
selectedValue={selectedValueAutocomplete}
|
||||
onChange={handleComboboxChangeAutocomplete}
|
||||
withAutocomplete
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1 @@
|
|||
export * from './components-test';
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
export * from './home';
|
||||
export * from './mint';
|
||||
export * from './svg-test';
|
||||
export * from './components-test';
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
import { Ethereum } from '@/integrations';
|
||||
|
||||
//TODO remove if we're not gonna validate ens on the client side
|
||||
export const validateEnsField = async (
|
||||
ensName: string,
|
||||
setError: (message: string) => void
|
||||
) => {
|
||||
const isValid = await Ethereum.validateEnsName(ensName);
|
||||
if (!isValid) setError('Invalid ENS name');
|
||||
else setError('');
|
||||
return isValid;
|
||||
};
|
||||
|
|
@ -1,30 +1,25 @@
|
|||
import { Card, Grid, Icon, IconButton } from '@/components';
|
||||
import { Card, Grid, Stepper } from '@/components';
|
||||
import { MintCardHeader } from '@/views/mint/mint-card';
|
||||
import { GithubButton } from './github-button';
|
||||
|
||||
export const GithubConnect: React.FC = () => (
|
||||
<Card.Container>
|
||||
<Card.Heading
|
||||
title="Connect GitHub"
|
||||
rightIcon={
|
||||
<IconButton
|
||||
aria-label="Add"
|
||||
colorScheme="gray"
|
||||
variant="link"
|
||||
icon={<Icon name="info" />}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Card.Body>
|
||||
<Grid css={{ rowGap: '$6' }}>
|
||||
<GithubButton />
|
||||
<Card.Text
|
||||
css={{ height: '$46h', width: '$95', fontSize: '$md', px: '$12' }}
|
||||
>
|
||||
<span>
|
||||
After connecting your GitHub, your repositories will show here.
|
||||
</span>
|
||||
</Card.Text>
|
||||
</Grid>
|
||||
</Card.Body>
|
||||
</Card.Container>
|
||||
);
|
||||
export const GithubConnect: React.FC = () => {
|
||||
const { prevStep } = Stepper.useContext();
|
||||
|
||||
return (
|
||||
<Card.Container>
|
||||
<MintCardHeader title="Connect GitHub" onClickBack={prevStep} />
|
||||
<Card.Body>
|
||||
<Grid css={{ rowGap: '$6' }}>
|
||||
<GithubButton />
|
||||
<Card.Text
|
||||
css={{ height: '$46h', width: '$95', fontSize: '$md', px: '$12' }}
|
||||
>
|
||||
<span>
|
||||
After connecting your GitHub, your repositories will show here.
|
||||
</span>
|
||||
</Card.Text>
|
||||
</Grid>
|
||||
</Card.Body>
|
||||
</Card.Container>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import { Card, ComboboxItem, Flex, Grid, Icon, Spinner } from '@/components';
|
||||
import { Input } from '@/components/core/input';
|
||||
import { useDebounce } from '@/hooks/use-debounce';
|
||||
import { useGithubStore } from '@/store';
|
||||
import { MintCardHeader } from '@/views/mint/mint-card';
|
||||
import { Mint } from '@/views/mint/mint.context';
|
||||
import React, { forwardRef, useRef, useState } from 'react';
|
||||
import React, { forwardRef, useState } from 'react';
|
||||
import { RepositoriesList } from './repositories-list';
|
||||
import { UserOrgsCombobox } from './users-orgs-combobox';
|
||||
|
||||
|
|
@ -46,14 +47,15 @@ export const GithubRepositoryConnection: React.FC = () => {
|
|||
|
||||
const { setGithubStep, setSelectedUserOrg } = Mint.useContext();
|
||||
|
||||
const timeOutRef = useRef<NodeJS.Timeout>();
|
||||
const setSearchValueDebounced = useDebounce(
|
||||
(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setSearchValue(event.target.value),
|
||||
500
|
||||
);
|
||||
|
||||
const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
event.stopPropagation();
|
||||
timeOutRef.current && clearTimeout(timeOutRef.current);
|
||||
timeOutRef.current = setTimeout(() => {
|
||||
setSearchValue(event.target.value);
|
||||
}, 500);
|
||||
setSearchValueDebounced(event);
|
||||
};
|
||||
|
||||
const handlePrevStepClick = () => {
|
||||
|
|
@ -73,7 +75,7 @@ export const GithubRepositoryConnection: React.FC = () => {
|
|||
<UserOrgsCombobox />
|
||||
<Input
|
||||
leftIcon="search"
|
||||
placeholder="Search"
|
||||
placeholder="Search repo"
|
||||
onChange={handleSearchChange}
|
||||
/>
|
||||
</Flex>
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ export const UserOrgsCombobox = () => {
|
|||
)}
|
||||
selectedValue={selectedUserOrg}
|
||||
onChange={handleUserOrgChange}
|
||||
leftIcon="github"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,14 +10,14 @@ export const MintStepper = () => {
|
|||
<Stepper.Root initialStep={1}>
|
||||
<Stepper.Container>
|
||||
<Stepper.Step>
|
||||
<MintStep header="Connect GitHub and select repository">
|
||||
<GithubStep />
|
||||
<MintStep header="Connect your Ethereum Wallet to mint an NFA">
|
||||
<WalletStep />
|
||||
</MintStep>
|
||||
</Stepper.Step>
|
||||
|
||||
<Stepper.Step>
|
||||
<MintStep header="Connect your Ethereum Wallet to mint an NFA">
|
||||
<WalletStep />
|
||||
<MintStep header="Connect GitHub and select repository">
|
||||
<GithubStep />
|
||||
</MintStep>
|
||||
</Stepper.Step>
|
||||
|
||||
|
|
|
|||
|
|
@ -15,9 +15,10 @@ export type MintContext = {
|
|||
appDescription: string;
|
||||
appLogo: string;
|
||||
logoColor: string;
|
||||
ens: DropdownItem;
|
||||
ens: ComboboxItem;
|
||||
domain: string;
|
||||
verifyNFA: boolean;
|
||||
ensError: string;
|
||||
setGithubStep: (step: number) => void;
|
||||
setNfaStep: (step: number) => void;
|
||||
setSelectedUserOrg: (userOrg: ComboboxItem) => void;
|
||||
|
|
@ -28,9 +29,10 @@ export type MintContext = {
|
|||
setAppDescription: (description: string) => void;
|
||||
setAppLogo: (logo: string) => void;
|
||||
setLogoColor: (color: string) => void;
|
||||
setEns: (ens: DropdownItem) => void;
|
||||
setEns: (ens: ComboboxItem) => void;
|
||||
setDomain: (domain: string) => void;
|
||||
setVerifyNFA: (verify: boolean) => void;
|
||||
setEnsError: (error: string) => void;
|
||||
};
|
||||
|
||||
const [MintProvider, useContext] = createContext<MintContext>({
|
||||
|
|
@ -62,10 +64,13 @@ export abstract class Mint {
|
|||
const [appDescription, setAppDescription] = useState('');
|
||||
const [appLogo, setAppLogo] = useState('');
|
||||
const [logoColor, setLogoColor] = useState('');
|
||||
const [ens, setEns] = useState({} as DropdownItem);
|
||||
const [ens, setEns] = useState({} as ComboboxItem);
|
||||
const [domain, setDomain] = useState('');
|
||||
const [verifyNFA, setVerifyNFA] = useState(true);
|
||||
|
||||
//Field validations
|
||||
const [ensError, setEnsError] = useState<string>('');
|
||||
|
||||
const setGithubStep = (step: number): void => {
|
||||
if (step > 0 && step <= 3) {
|
||||
setGithubStepContext(step);
|
||||
|
|
@ -88,6 +93,7 @@ export abstract class Mint {
|
|||
ens,
|
||||
domain,
|
||||
verifyNFA,
|
||||
ensError,
|
||||
setSelectedUserOrg,
|
||||
setGithubStep,
|
||||
setNfaStep,
|
||||
|
|
@ -101,6 +107,7 @@ export abstract class Mint {
|
|||
setEns,
|
||||
setDomain,
|
||||
setVerifyNFA,
|
||||
setEnsError,
|
||||
}}
|
||||
>
|
||||
<TransactionProvider
|
||||
|
|
|
|||
|
|
@ -1,37 +1,35 @@
|
|||
import { Dropdown, DropdownItem, Form } from '@/components';
|
||||
import { Combobox, ComboboxItem, Form } from '@/components';
|
||||
import { ensActions, useAppDispatch, useEnsStore } from '@/store';
|
||||
import { Mint } from '@/views/mint/mint.context';
|
||||
|
||||
// TODO remove after integration with wallet
|
||||
const ensList: DropdownItem[] = [
|
||||
{
|
||||
value: 'fleek.eth',
|
||||
label: 'fleek.eth',
|
||||
},
|
||||
{
|
||||
value: 'ens.eth',
|
||||
label: 'ens.eth',
|
||||
},
|
||||
{
|
||||
value: 'cami.eth',
|
||||
label: 'cami.eth',
|
||||
},
|
||||
];
|
||||
import { useAccount } from 'wagmi';
|
||||
|
||||
export const EnsField = () => {
|
||||
const { ens, setEns } = Mint.useContext();
|
||||
const { ens, ensError, setEns } = Mint.useContext();
|
||||
const { state, ensNames } = useEnsStore();
|
||||
const dispatch = useAppDispatch();
|
||||
const { address } = useAccount();
|
||||
|
||||
const handleEnsChange = (item: DropdownItem) => {
|
||||
if (state === 'idle' && address) {
|
||||
dispatch(ensActions.fetchEnsNamesThunk(address));
|
||||
}
|
||||
|
||||
const handleEnsChange = (item: ComboboxItem) => {
|
||||
setEns(item);
|
||||
};
|
||||
|
||||
return (
|
||||
<Form.Field css={{ flex: 1 }}>
|
||||
<Form.Label>ENS</Form.Label>
|
||||
<Dropdown
|
||||
items={ensList}
|
||||
<Combobox
|
||||
items={ensNames.map((ens) => ({
|
||||
label: ens,
|
||||
value: ens,
|
||||
}))}
|
||||
selectedValue={ens}
|
||||
onChange={handleEnsChange}
|
||||
withAutocomplete
|
||||
/>
|
||||
{ensError && <Form.Error>{ensError}</Form.Error>}
|
||||
</Form.Field>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,12 +1,20 @@
|
|||
import { Button, Card, Grid, Icon, Stepper } from '@/components';
|
||||
import { MintCardHeader } from '../mint-card';
|
||||
import { Card, Grid, Icon, IconButton } from '@/components';
|
||||
import { ConnectWalletButton } from './connect-wallet-button';
|
||||
|
||||
export const WalletStep = () => {
|
||||
const { prevStep } = Stepper.useContext();
|
||||
return (
|
||||
<Card.Container>
|
||||
<MintCardHeader title="Connect Wallet" onClickBack={prevStep} />
|
||||
<Card.Heading
|
||||
title="Connect Wallet"
|
||||
rightIcon={
|
||||
<IconButton
|
||||
aria-label="Add"
|
||||
colorScheme="gray"
|
||||
variant="link"
|
||||
icon={<Icon name="info" />}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Card.Body>
|
||||
<Grid css={{ rowGap: '$6' }}>
|
||||
<ConnectWalletButton />
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ module.exports = {
|
|||
borderRadius: {
|
||||
xhl: '1.25rem',
|
||||
},
|
||||
maxWidth: {
|
||||
70: '70%',
|
||||
},
|
||||
space: {
|
||||
'1h': '0.375rem',
|
||||
},
|
||||
|
|
|
|||
115
ui/yarn.lock
115
ui/yarn.lock
|
|
@ -1415,7 +1415,7 @@
|
|||
dependencies:
|
||||
"@ethersproject/bignumber" "^5.7.0"
|
||||
|
||||
"@ethersproject/contracts@5.7.0":
|
||||
"@ethersproject/contracts@5.7.0", "@ethersproject/contracts@^5.7.0":
|
||||
version "5.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.7.0.tgz#c305e775abd07e48aa590e1a877ed5c316f8bd1e"
|
||||
integrity sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==
|
||||
|
|
@ -1518,7 +1518,7 @@
|
|||
dependencies:
|
||||
"@ethersproject/logger" "^5.7.0"
|
||||
|
||||
"@ethersproject/providers@5.7.2", "@ethersproject/providers@^5.7.2":
|
||||
"@ethersproject/providers@5.7.2", "@ethersproject/providers@^5.7.0", "@ethersproject/providers@^5.7.2":
|
||||
version "5.7.2"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.2.tgz#f8b1a4f275d7ce58cf0a2eec222269a08beb18cb"
|
||||
integrity sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==
|
||||
|
|
@ -1617,7 +1617,7 @@
|
|||
"@ethersproject/rlp" "^5.7.0"
|
||||
"@ethersproject/signing-key" "^5.7.0"
|
||||
|
||||
"@ethersproject/units@5.7.0":
|
||||
"@ethersproject/units@5.7.0", "@ethersproject/units@^5.7.0":
|
||||
version "5.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.7.0.tgz#637b563d7e14f42deeee39245275d477aae1d8b1"
|
||||
integrity sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==
|
||||
|
|
@ -1626,7 +1626,7 @@
|
|||
"@ethersproject/constants" "^5.7.0"
|
||||
"@ethersproject/logger" "^5.7.0"
|
||||
|
||||
"@ethersproject/wallet@5.7.0":
|
||||
"@ethersproject/wallet@5.7.0", "@ethersproject/wallet@^5.7.0":
|
||||
version "5.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.7.0.tgz#4e5d0790d96fe21d61d38fb40324e6c7ef350b2d"
|
||||
integrity sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==
|
||||
|
|
@ -5373,6 +5373,26 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.4, ajv
|
|||
json-schema-traverse "^0.4.1"
|
||||
uri-js "^4.2.2"
|
||||
|
||||
alchemy-sdk@^2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/alchemy-sdk/-/alchemy-sdk-2.5.0.tgz#20813b02eded16e9cbc56c5fc8259684dc324eb9"
|
||||
integrity sha512-pgwyPiAmUp4uaBkkpdnlHxjB7yjeO88VhIIFo5aCiuPAxgpB5nKRqBQw2XXqiZEUF4xU1ybZ2s1ESQ6k/hc2MQ==
|
||||
dependencies:
|
||||
"@ethersproject/abi" "^5.7.0"
|
||||
"@ethersproject/abstract-provider" "^5.7.0"
|
||||
"@ethersproject/bignumber" "^5.7.0"
|
||||
"@ethersproject/bytes" "^5.7.0"
|
||||
"@ethersproject/contracts" "^5.7.0"
|
||||
"@ethersproject/hash" "^5.7.0"
|
||||
"@ethersproject/networks" "^5.7.0"
|
||||
"@ethersproject/providers" "^5.7.0"
|
||||
"@ethersproject/units" "^5.7.0"
|
||||
"@ethersproject/wallet" "^5.7.0"
|
||||
"@ethersproject/web" "^5.7.0"
|
||||
axios "^0.26.1"
|
||||
sturdy-websocket "^0.2.1"
|
||||
websocket "^1.0.34"
|
||||
|
||||
ansi-align@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59"
|
||||
|
|
@ -5756,6 +5776,13 @@ axios@^0.21.0:
|
|||
dependencies:
|
||||
follow-redirects "^1.14.0"
|
||||
|
||||
axios@^0.26.1:
|
||||
version "0.26.1"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.26.1.tgz#1ede41c51fcf51bbbd6fd43669caaa4f0495aaa9"
|
||||
integrity sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==
|
||||
dependencies:
|
||||
follow-redirects "^1.14.8"
|
||||
|
||||
babel-loader@^8.0.0, babel-loader@^8.3.0:
|
||||
version "8.3.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.3.0.tgz#124936e841ba4fe8176786d6ff28add1f134d6a8"
|
||||
|
|
@ -7086,6 +7113,14 @@ cyclist@^1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
|
||||
integrity sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A==
|
||||
|
||||
d@1, d@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a"
|
||||
integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==
|
||||
dependencies:
|
||||
es5-ext "^0.10.50"
|
||||
type "^1.0.1"
|
||||
|
||||
dashdash@^1.12.0:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
|
||||
|
|
@ -7716,11 +7751,29 @@ es-to-primitive@^1.2.1:
|
|||
is-date-object "^1.0.1"
|
||||
is-symbol "^1.0.2"
|
||||
|
||||
es5-ext@^0.10.35, es5-ext@^0.10.50:
|
||||
version "0.10.62"
|
||||
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5"
|
||||
integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==
|
||||
dependencies:
|
||||
es6-iterator "^2.0.3"
|
||||
es6-symbol "^3.1.3"
|
||||
next-tick "^1.1.0"
|
||||
|
||||
es5-shim@^4.5.13:
|
||||
version "4.6.7"
|
||||
resolved "https://registry.yarnpkg.com/es5-shim/-/es5-shim-4.6.7.tgz#bc67ae0fc3dd520636e0a1601cc73b450ad3e955"
|
||||
integrity sha512-jg21/dmlrNQI7JyyA2w7n+yifSxBng0ZralnSfVZjoCawgNTCnS+yBCyVM9DL5itm7SUnDGgv7hcq2XCZX4iRQ==
|
||||
|
||||
es6-iterator@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"
|
||||
integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==
|
||||
dependencies:
|
||||
d "1"
|
||||
es5-ext "^0.10.35"
|
||||
es6-symbol "^3.1.1"
|
||||
|
||||
es6-promise@^4.0.3:
|
||||
version "4.2.8"
|
||||
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
|
||||
|
|
@ -7738,6 +7791,14 @@ es6-shim@^0.35.5:
|
|||
resolved "https://registry.yarnpkg.com/es6-shim/-/es6-shim-0.35.7.tgz#db00f1cbb7d4de70b50dcafa45b157e9ba28f5d2"
|
||||
integrity sha512-baZkUfTDSx7X69+NA8imbvGrsPfqH0MX7ADdIDjqwsI8lkTgLIiD2QWrUCSGsUQ0YMnSCA/4pNgSyXdnLHWf3A==
|
||||
|
||||
es6-symbol@^3.1.1, es6-symbol@^3.1.3:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18"
|
||||
integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==
|
||||
dependencies:
|
||||
d "^1.0.1"
|
||||
ext "^1.1.2"
|
||||
|
||||
esbuild-android-64@0.15.18:
|
||||
version "0.15.18"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz#20a7ae1416c8eaade917fb2453c1259302c637a5"
|
||||
|
|
@ -8347,6 +8408,13 @@ express@^4.17.1:
|
|||
utils-merge "1.0.1"
|
||||
vary "~1.1.2"
|
||||
|
||||
ext@^1.1.2:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f"
|
||||
integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==
|
||||
dependencies:
|
||||
type "^2.7.2"
|
||||
|
||||
extend-shallow@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
|
||||
|
|
@ -8667,7 +8735,7 @@ focus-lock@^0.8.0:
|
|||
dependencies:
|
||||
tslib "^1.9.3"
|
||||
|
||||
follow-redirects@^1.14.0:
|
||||
follow-redirects@^1.14.0, follow-redirects@^1.14.8:
|
||||
version "1.15.2"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
|
||||
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
|
||||
|
|
@ -11288,6 +11356,11 @@ nested-error-stacks@^2.0.0, nested-error-stacks@^2.1.0:
|
|||
resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz#26c8a3cee6cc05fbcf1e333cd2fc3e003326c0b5"
|
||||
integrity sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==
|
||||
|
||||
next-tick@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb"
|
||||
integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==
|
||||
|
||||
nice-try@^1.0.4:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
||||
|
|
@ -13951,6 +14024,11 @@ strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.
|
|||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
|
||||
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
|
||||
|
||||
sturdy-websocket@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/sturdy-websocket/-/sturdy-websocket-0.2.1.tgz#20a58fd53372ef96eaa08f3c61c91a10b07c7c05"
|
||||
integrity sha512-NnzSOEKyv4I83qbuKw9ROtJrrT6Z/Xt7I0HiP/e6H6GnpeTDvzwGIGeJ8slai+VwODSHQDooW2CAilJwT9SpRg==
|
||||
|
||||
style-loader@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.3.0.tgz#828b4a3b3b7e7aa5847ce7bae9e874512114249e"
|
||||
|
|
@ -14466,6 +14544,16 @@ type-is@~1.6.18:
|
|||
media-typer "0.3.0"
|
||||
mime-types "~2.1.24"
|
||||
|
||||
type@^1.0.1:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0"
|
||||
integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==
|
||||
|
||||
type@^2.7.2:
|
||||
version "2.7.2"
|
||||
resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0"
|
||||
integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==
|
||||
|
||||
typed-array-length@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb"
|
||||
|
|
@ -15111,6 +15199,18 @@ websocket-extensions@>=0.1.1:
|
|||
resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42"
|
||||
integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==
|
||||
|
||||
websocket@^1.0.34:
|
||||
version "1.0.34"
|
||||
resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.34.tgz#2bdc2602c08bf2c82253b730655c0ef7dcab3111"
|
||||
integrity sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==
|
||||
dependencies:
|
||||
bufferutil "^4.0.1"
|
||||
debug "^2.2.0"
|
||||
es5-ext "^0.10.50"
|
||||
typedarray-to-buffer "^3.1.5"
|
||||
utf-8-validate "^5.0.2"
|
||||
yaeti "^0.0.6"
|
||||
|
||||
whatwg-url@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
|
||||
|
|
@ -15303,6 +15403,11 @@ y18n@^5.0.5:
|
|||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
|
||||
integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
|
||||
|
||||
yaeti@^0.0.6:
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577"
|
||||
integrity sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==
|
||||
|
||||
yallist@^3.0.2:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
|
||||
|
|
|
|||
Loading…
Reference in New Issue