Skip to content

Commit

Permalink
Create page for LSP4 Metadata encoding
Browse files Browse the repository at this point in the history
  • Loading branch information
b00ste committed Jul 10, 2024
1 parent d6ac5d0 commit 267ad25
Show file tree
Hide file tree
Showing 38 changed files with 918 additions and 73 deletions.
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SHARED_KEY=Y2hpZTZwaGUxb29naDhpZVp1b2szZXNoaWV4YWhTaDF6YWk2c2hhaHNoYWhwaHVlR2F6b3RhZTJqb3VyNWViYQ==
2 changes: 1 addition & 1 deletion components/AddressButtons/AddressButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* @author Felix Hildebrandt <fhildeb>
*/
import React, { useContext } from 'react';
import { NetworkContext } from '../../contexts/NetworksContext';
import { NetworkContext } from '@/contexts/NetworksContext';

interface Props {
address: string;
Expand Down
8 changes: 4 additions & 4 deletions components/AddressInfos/AddressInfos.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import React, { useState, useContext, useEffect } from 'react';
import Skeleton from 'react-loading-skeleton';
import { ERC725YDataKeys } from '@lukso/lsp-smart-contracts';

import { NetworkContext } from '../../contexts/NetworksContext';
import useWeb3 from '../../hooks/useWeb3';
import { NetworkContext } from '@/contexts/NetworksContext';
import useWeb3 from '@/hooks/useWeb3';
import {
EXPLORER_BASE_URL,
LSP1_DELEGATE_VERSIONS,
LSP1_GRAVE_FORWARDER,
UP_RECOVERY_ADDRESSES,
} from '../../globals';
import { checkInterface, getData, checkIsGnosisSafe } from '../../utils/web3';
} from '@/globals';
import { checkInterface, getData, checkIsGnosisSafe } from '@/utils/web3';

import LSP7Artifact from '@lukso/lsp-smart-contracts/artifacts/LSP7DigitalAsset.json';
import { AbiItem } from 'web3-utils';
Expand Down
6 changes: 3 additions & 3 deletions components/ContractOwner/ContractOwner.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { useState, useEffect } from 'react';
import ERC725Account from '@lukso/lsp-smart-contracts/artifacts/LSP0ERC725Account.json';
import useWeb3 from '../../hooks/useWeb3';
import useWeb3 from '@/hooks/useWeb3';

import { eip165ABI } from '../../constants';
import { eip165ABI } from '@/constants';
import { AbiItem, isAddress } from 'web3-utils';
import { INTERFACE_IDS } from '@lukso/lsp-smart-contracts';
import AddressButtons from '../AddressButtons';
import AddressButtons from '@/components/AddressButtons';

type Props = {
contractAddress: string;
Expand Down
6 changes: 3 additions & 3 deletions components/ControllersList/ControllersList.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React, { useEffect, useState } from 'react';
import ERC725, { encodeArrayKey, encodeKeyName } from '@erc725/erc725.js';

import { getDataBatch } from '../../utils/web3';
import useWeb3 from '../../hooks/useWeb3';
import AddressInfos from '../AddressInfos';
import { getDataBatch } from '@/utils/web3';
import useWeb3 from '@/hooks/useWeb3';
import AddressInfos from '@/components/AddressInfos';
import { ERC725YDataKeys } from '@lukso/lsp-smart-contracts';

interface Props {
Expand Down
8 changes: 4 additions & 4 deletions components/DataKeysTable/DataKeysTable.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import React, { useEffect, useState } from 'react';
import { ERC725JSONSchema } from '@erc725/erc725.js';

import AddressButtons from '../AddressButtons';
import ValueTypeDecoder from '../ValueTypeDecoder';
import AddressButtons from '@/components/AddressButtons';
import ValueTypeDecoder from '@/components/ValueTypeDecoder';

import ProfileSchema from './ProfileSchema.json';
import AssetSchema from './AssetSchema.json';
import LSP8Schema from './LSP8Schema.json';

import { SCHEMA_DOCS_LINKS, SchemaName } from './schemas';

import { getDataBatch } from '../../utils/web3';
import { getDataBatch } from '@/utils/web3';

import useWeb3 from '../../hooks/useWeb3';
import useWeb3 from '@/hooks/useWeb3';

interface Props {
address: string;
Expand Down
4 changes: 2 additions & 2 deletions components/Decode/Decode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import Web3 from 'web3';
import {
TRANSACTION_SELECTORS,
TRANSACTION_TYPES,
} from '../../interfaces/transaction';
import ErrorMessage from '../ErrorMessage/ErrorMessage';
} from '@/interfaces/transaction';
import ErrorMessage from '@/components/ErrorMessage';
import styles from './Decode.module.scss';

interface Props {
Expand Down
2 changes: 1 addition & 1 deletion components/Encode/Encode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import EncodeSetData from './components/EncodeSetData';
import EncodeTransferOwnership from './components/EncodeTransferOwnership';
import styles from './Encode.module.scss';

import { TRANSACTION_TYPES } from '../../interfaces/transaction';
import { TRANSACTION_TYPES } from '@/interfaces/transaction';

interface Props {
web3: Web3;
Expand Down
2 changes: 1 addition & 1 deletion components/Encode/components/EncodeExecute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useState } from 'react';
import Web3 from 'web3';
import EncodedPayload from './EncodedPayload';
import ERC725Account from '@lukso/lsp-smart-contracts/artifacts/LSP0ERC725Account.json';
import ErrorMessage from '../../ErrorMessage';
import ErrorMessage from '@/components/ErrorMessage';
import styles from './EncodeExecute.module.scss';

interface Props {
Expand Down
2 changes: 1 addition & 1 deletion components/Encode/components/EncodeSetData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useState } from 'react';
import Web3 from 'web3';
import EncodedPayload from './EncodedPayload';
import ERC725Account from '@lukso/lsp-smart-contracts/artifacts/LSP0ERC725Account.json';
import ErrorMessage from '../../ErrorMessage';
import ErrorMessage from '@/components/ErrorMessage';
import { AbiItem } from 'web3-utils';
import styles from './EncodeSetData.module.scss';

Expand Down
2 changes: 1 addition & 1 deletion components/Encode/components/EncodeTransferOwnership.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useState } from 'react';
import Web3 from 'web3';
import EncodedPayload from './EncodedPayload';
import ERC725Account from '@lukso/lsp-smart-contracts/artifacts/LSP0ERC725Account.json';
import ErrorMessage from '../../ErrorMessage';
import ErrorMessage from '@/components/ErrorMessage';
import { Contract } from 'web3-eth-contract';
import styles from './EncodeTransferOwnership.module.scss';
interface Props {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { useState } from 'react';

import LSP6KeyManager from '@lukso/lsp-smart-contracts/artifacts/LSP6KeyManager.json';
import useWeb3 from '../../hooks/useWeb3';
import useWeb3 from '@/hooks/useWeb3';

const KeyManagerNonceChecker: React.FC = () => {
const web3 = useWeb3();
Expand Down
2 changes: 1 addition & 1 deletion components/KeyManagerPermissions/KeyManagerPermissions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { useState } from 'react';
import ERC725 from '@erc725/erc725.js';
import PermissionsBtns from '../PermissionsBtns';
import PermissionsBtns from '@/components/PermissionsBtns';

const KeyManagerPermissions: React.FC = () => {
const initialEncodedPermissions =
Expand Down
9 changes: 9 additions & 0 deletions components/NavBar/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,15 @@ const NavBar: React.FC = () => {
📖 LSP2 Encoder
</a>
</Link>
<Link href="/lsp4-metadata-encoder">
<a
className={`navbar-item ${
router.pathname === '/lsp4-metadata-encoder' && 'has-text-link'
}`}
>
📖 LSP4 Metadata Encoder
</a>
</Link>
</div>

<div className="navbar-end">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React, { useContext, useState } from 'react';
import { useRouter } from 'next/router';
import { INetwork, NetworkContext } from '../../../../contexts/NetworksContext';
import { INetwork, NetworkContext } from '@/contexts/NetworksContext';

import { RPC_URL } from '../../../../globals';
import { NetworkName } from '../../../../types/network';
import { RPC_URL } from '@/globals';
import { NetworkName } from '@/types/network';

const luksoChains: INetwork[] = [
{
Expand Down
4 changes: 2 additions & 2 deletions components/SampleAddressInput/SampleAddressInput.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useContext } from 'react';

import { NetworkContext } from '../../contexts/NetworksContext';
import { SAMPLE_ADDRESS } from '../../constants';
import { NetworkContext } from '@/contexts/NetworksContext';
import { SAMPLE_ADDRESS } from '@/constants';

enum AddressType {
UP = 'UP',
Expand Down
10 changes: 5 additions & 5 deletions components/ValueTypeDecoder/ValueTypeDecoder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
*/
import React, { useState, useEffect } from 'react';
import { ERC725, ERC725JSONSchema } from '@erc725/erc725.js';
import AddressButtons from '../AddressButtons';
import ControllersList from '../ControllersList';
import { LUKSO_IPFS_BASE_URL } from '../../globals';
import AddressButtons from '@/components/AddressButtons';
import ControllersList from '@/components/ControllersList';
import { LUKSO_IPFS_BASE_URL } from '@/globals';

import useWeb3 from '../../hooks/useWeb3';
import useWeb3 from '@/hooks/useWeb3';

import { DecodeDataOutput } from '@erc725/erc725.js/build/main/src/types/decodeData';
import AddressInfos from '../AddressInfos';
import AddressInfos from '@/components/AddressInfos';

import { LSP4_TOKEN_TYPES } from '@lukso/lsp-smart-contracts';

Expand Down
4 changes: 2 additions & 2 deletions contexts/NetworksContext.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createContext, useState } from 'react';
import { RPC_URL } from '../globals';
import { NetworkName } from '../types/network';
import { RPC_URL } from '@/globals';
import { NetworkName } from '@/types/network';
export interface INetwork {
name: NetworkName;
rpc: string;
Expand Down
2 changes: 1 addition & 1 deletion hooks/useWeb3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/
import { useState, useEffect, useContext } from 'react';
import Web3 from 'web3';
import { NetworkContext } from '../contexts/NetworksContext';
import { NetworkContext } from '@/contexts/NetworksContext';

export default function useWeb3() {
const [web3Info, setWeb3Info] = useState<Web3>();
Expand Down
26 changes: 26 additions & 0 deletions ipfs/authenticated-formdata-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Buffer } from 'buffer';
import { sign } from './sign';
import { FormDataPostHeaders } from './formdata-base-client';
import { CustomHeaderFormDataUploader } from './ipfs-formdata-clients';

const sharedKey = Buffer.from(
process.env.SHARED_KEY as string,
'base64',
).toString();

export class AuthenticatedFormDataUploader extends CustomHeaderFormDataUploader {
async getHeaders(_dataContent: FormData, _meta?: FormDataPostHeaders) {
const jwt = await this.getToken();
return { Authorization: `Bearer ${jwt}` };
}
resolveUrl(result: any): string {
return `ipfs://${result.IpfsHash}`;
}
async getToken(): Promise<string> {
const now = Date.now();
return await sign(
{ iss: 'extension', iat: now / 1000, exp: (now + 120_000) / 1000 },
sharedKey || '',
);
}
}
144 changes: 144 additions & 0 deletions ipfs/formdata-base-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
const NOT_IMPLEMENTED = 'Not implemented';

interface PinataPinResponse {
IpfsHash: string;
}

export interface AssetBuffer {
buffer: Buffer;
mimeType: string;
}

export type FormDataPostHeaders = Record<string, string | number | any>;
export type FormDataRequestOptions = {
maxContentLength?: number;
maxBodyLength?: number;
withCredentials?: boolean;
headers?: FormDataPostHeaders;
};

export const handleError = (error: any) => {
if (
error &&
error.response &&
error.response &&
error.response.data &&
error.response.data.error
) {
return error.response.data.error;
} else if (error.data && error.data.error) {
return error.data.error;
} else if (error.response && error.response.error) {
return error.response.error;
}
return error;
};

export class BaseFormDataUploader {
// Already refactored several times, but still too complex since it needs
// to handle both node and browser types.
private populate(
dataContent: FormData,
data: any,
meta?: FormDataPostHeaders,
): FormDataPostHeaders | undefined {
if (!('on' in data) && typeof data !== 'string') {
if ('size' in data && 'type' in data) {
const blob = data;
meta = {
'content-type': blob.type,
...(blob.name ? { name: blob.name } : {}),
};
dataContent.append('file', blob);
} else if ('buffer' in data && 'mimeType' in data) {
const assetBuffer = data as AssetBuffer;
meta = { 'content-type': assetBuffer.mimeType };
dataContent.append(
'file',
new (global.Blob || Blob)([assetBuffer.buffer]),
);
} else if (Buffer.isBuffer(data)) {
dataContent.append('file', new (global.Blob || Blob)([data]));
} else if ('on' in data && 'pipe' in data) {
dataContent.append('file', data);
} else {
throw new Error('Unknown upload data format');
}
} else {
dataContent.append('file', data);
}
return meta;
}
async upload(data: any, meta?: FormDataPostHeaders): Promise<string> {
const dataContent = new FormData();
meta = this.populate(dataContent, data, meta);
await this.addMetadata(dataContent as FormData, meta);
const options = await this.getRequestOptions(dataContent as FormData, meta);
// This needs to be in a different files for testing with jest to work
// property. Internal access to internal methods in a file cannot be patched.
return this.resolveUrl(
await this.uploadFormData(options, dataContent as FormData),
);
}
async addMetadata(_dataContent: FormData, _meta?: FormDataPostHeaders) {
return;
}
async getRequestOptions(
_dataContent: FormData,
_meta?: FormDataPostHeaders,
): Promise<FormDataRequestOptions> {
return {
maxContentLength: Number.MAX_SAFE_INTEGER,
maxBodyLength: Number.MAX_SAFE_INTEGER,
withCredentials: true,
};
}
getPostEndpoint(): string {
throw new Error(NOT_IMPLEMENTED);
}
resolveUrl(_result: any): string {
throw new Error(NOT_IMPLEMENTED);
}
uploadFormData(
requestOptions: FormDataRequestOptions,
dataContent: FormData,
): Promise<any> {
const input = {
method: 'POST',
...requestOptions,
} as RequestInit;
input.headers = { ...input.headers /* ...headers */ };
return (globalThis.fetch || fetch)(this.getEndpoint(), {
...input,
body: dataContent as any,
})
.then((response) => {
if (response.status !== 200) {
return response.text().then((text) => {
let error = text;
try {
error = JSON.parse(text);
} catch {
// Ignore
}
error = (error as any).error || error;
throw new Error(
`unknown server response while pinning File to IPFS: ${
error || response.status
}`,
);
});
}
return response.json() as Promise<PinataPinResponse>;
})
.catch(function (error) {
throw handleError(error);
});
}
async getToken(): Promise<string> {
throw new Error(NOT_IMPLEMENTED);
}
getEndpoint(): string {
throw new Error(NOT_IMPLEMENTED);
}
}
Loading

0 comments on commit 267ad25

Please sign in to comment.