Skip to content
This repository was archived by the owner on Jun 23, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ rusqlite = { version = "0.32.1", features = ["serde_json"] }
secp256k1 = { version = "0.29.1", features = ["recovery", "rand", "global-context"] }
serde = { version = "1.0.208", features = ["derive"] }
serde_json = "1.0.125"
serde_with = "3.11.0"
sha3 = "0.10.8"
surrealdb = { version = "1.5.4" }
tokio = { version = "1.39.2", features = ["macros", "rt", "rt-multi-thread", "time"] }
Expand Down
16 changes: 0 additions & 16 deletions sdk/apps/nft-minting-example/contracts/MyNFT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,8 @@ pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@quible/verifier-solidity-sdk/contracts/QuibleVerifier.sol";

function bytesToHexString(bytes memory data) pure returns (string memory) {
bytes memory converted = new bytes(data.length * 2);

bytes memory _base = "0123456789abcdef";

for (uint256 i = 0; i < data.length; i++) {
converted[i * 2] = _base[uint8(data[i]) / _base.length];
converted[i * 2 + 1] = _base[uint8(data[i]) % _base.length];
}

return string(abi.encodePacked("0x", converted));
}

contract MyNFT is ERC721, ERC721Enumerable, Ownable {
uint256 private _nextTokenId;
bytes32 public quirkleRoot;
Expand Down
7 changes: 5 additions & 2 deletions sdk/apps/nft-minting-example/next.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
};
experimental: {
esmExternals: 'loose',
},
}

module.exports = nextConfig;
module.exports = nextConfig
84 changes: 48 additions & 36 deletions sdk/apps/nft-minting-example/src/components/LaunchToken.tsx
Original file line number Diff line number Diff line change
@@ -1,55 +1,67 @@
import { useCallback, useState } from "react";
import { waitForTransactionReceipt } from "@wagmi/core";
import { useDeployContract, useConfig, useSignMessage } from "wagmi";
import MyNFTArtifacts from "../../artifacts/contracts/MyNFT.sol/MyNFT.json";
import Link from "next/link";
import ReactEditList, * as REL from "react-edit-list";
import { QuibleProvider } from '@quible/js-sdk';
import { useCallback, useState } from 'react'
import { waitForTransactionReceipt } from '@wagmi/core'
import { useDeployContract, useConfig, useSignMessage } from 'wagmi'
import MyNFTArtifacts from '../../artifacts/contracts/MyNFT.sol/MyNFT.json'
import Link from 'next/link'
import ReactEditList, * as REL from 'react-edit-list'
import { QuibleSigner, QuibleProvider } from '@quible/js-sdk'
import { convertHexStringToUint8Array } from '@quible/js-sdk/lib/utils'

const quibleProvider = new QuibleProvider('http://localhost:9013')

const schema: REL.Schema = [
{ name: "id", type: "id" },
{ name: "address", type: "string" },
];
{ name: 'id', type: 'id' },
{ name: 'address', type: 'string' },
]

const LaunchToken = (props: {
accountAddress: string;
}) => {
const [contractAddress, setContractAddress] = useState<string | null>(null);
const [isPending, setIsPending] = useState(false);
const [accessList, setAccessList] = useState<string[]>([props.accountAddress]);
const config = useConfig();
const LaunchToken = (props: { accountAddress: string }) => {
const [contractAddress, setContractAddress] = useState<string | null>(null)
const [isPending, setIsPending] = useState(false)
const [accessList, setAccessList] = useState<string[]>([props.accountAddress])
const config = useConfig()

const { signMessageAsync } = useSignMessage()
const { deployContractAsync } = useDeployContract();
const { deployContractAsync } = useDeployContract()

const handleDeployContract = useCallback(async () => {
setIsPending(true);
const wallet = quibleProvider.getWallet(props.accountAddress);
setIsPending(true)
const signer = QuibleSigner.fromAddress(
{ raw: props.accountAddress },
(message) =>
signMessageAsync({ message: { raw: message } }).then(
convertHexStringToUint8Array,
),
)

const quirkleRoot = await wallet.createQuirkle({
members: accessList,
proofTtl: 86400,
signMessage: (message) => signMessageAsync({message: { raw: message }})
const wallet = quibleProvider.getWallet(signer)

const identity = await wallet.createIdentity({
claims: accessList,
certificateLifespan: 86400,
})

const hash = await deployContractAsync({
abi: MyNFTArtifacts.abi,
bytecode: MyNFTArtifacts.bytecode as unknown as `0x${string}`,
args: [props.accountAddress, `0x${quirkleRoot.toHex()}`],
});
args: [props.accountAddress, identity.id.toHexString()],
})

const { contractAddress: newContractAddress } =
await waitForTransactionReceipt(config, { hash });
await waitForTransactionReceipt(config, { hash })

setContractAddress(newContractAddress as unknown as string);
setIsPending(false);
}, [props.accountAddress, accessList, config, signMessageAsync, deployContractAsync]);
setContractAddress(newContractAddress as unknown as string)
setIsPending(false)
}, [
props.accountAddress,
accessList,
config,
signMessageAsync,
deployContractAsync,
])

const handleAccessListChange = (list: REL.Row[]) => {
setAccessList(list.map((row) => row.id as string));
};
setAccessList(list.map((row) => row.id as string))
}

return (
<div>
Expand All @@ -71,7 +83,7 @@ const LaunchToken = (props: {
<div>
<Link
href={`/tokens/${contractAddress}`}
style={{ textDecoration: "underline", color: "blue" }}
style={{ textDecoration: 'underline', color: 'blue' }}
>
Contract deployed at <code>{contractAddress}</code>
</Link>
Expand All @@ -80,7 +92,7 @@ const LaunchToken = (props: {
</>
)}
</div>
);
};
)
}

export default LaunchToken;
export default LaunchToken
76 changes: 43 additions & 33 deletions sdk/apps/nft-minting-example/src/components/Minting.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,76 +2,86 @@ import { useCallback } from 'react'
import { waitForTransactionReceipt, readContract } from '@wagmi/core'
import { useReadContract, useWriteContract, useConfig } from 'wagmi'
import MyNFTArtifacts from '../../artifacts/contracts/MyNFT.sol/MyNFT.json'
import { convertHexStringToUint8Array } from '@quible/js-sdk/lib/utils'

const Minting = (props: { accountAddress: string, tokenAddress: string }) => {
const Minting = (props: { accountAddress: string; tokenAddress: string }) => {
const config = useConfig()
const { data: hash, writeContractAsync } = useWriteContract()

const { data, isSuccess, refetch } = useReadContract({
abi: MyNFTArtifacts.abi,
address: props.tokenAddress as unknown as `0x${string}`,
functionName: 'balanceOf',
args: [props.accountAddress]
args: [props.accountAddress],
})

const handleMint = useCallback(async () => {
console.log('querying quirkle root', props.tokenAddress);
console.log('querying quirkle root', props.tokenAddress)
const quirkleRoot = await readContract(config, {
abi: MyNFTArtifacts.abi,
address: props.tokenAddress as `0x${string}`,
functionName: 'getQuirkleRoot'
functionName: 'getQuirkleRoot',
})

console.log('got quirkle root', quirkleRoot);
console.log('got quirkle root', quirkleRoot)

const response = await fetch(
'http://localhost:9013',
{
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
jsonrpc: '2.0',
method: 'quible_requestProof',
id: 67,
params: [quirkleRoot, props.accountAddress.toLowerCase(), 0]
})
}
)
const response = await fetch('http://localhost:9013', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
jsonrpc: '2.0',
method: 'quible_requestCertificate',
id: 67,
params: [
[...convertHexStringToUint8Array(quirkleRoot as string)],
[...convertHexStringToUint8Array(props.accountAddress.toLowerCase())],
],
}),
})

const body = await response.json();
const body = await response.json()

console.log('got body', body);
console.log('got body', body)

if (body.error) {
throw new Error(JSON.stringify(body.error));
throw new Error(JSON.stringify(body.error))
}

const {signature, expires_at} = body.result;
const {
signature,
details: { expires_at },
} = body.result

const hash = await writeContractAsync({
abi: MyNFTArtifacts.abi,
address: props.tokenAddress as unknown as `0x${string}`,
functionName: 'safeMint',
args: [props.accountAddress, expires_at, `0x${signature}`]
args: [props.accountAddress, BigInt(expires_at), `0x${signature}`],
})

await waitForTransactionReceipt(config, { hash })
refetch()
}, [props.accountAddress, props.tokenAddress, config, refetch, writeContractAsync]);
}, [
props.accountAddress,
props.tokenAddress,
config,
refetch,
writeContractAsync,
])

if (!isSuccess) { return <div>Loading...</div> }
if (!isSuccess) {
return <div>Loading...</div>
}

return (
<div>
<button onClick={handleMint}>Mint</button>
<p>
total NFT count: {`${data}`}
</p>
<p>total NFT count: {`${data}`}</p>
{hash && <p>Transaction hash: {hash}</p>}
</div>
);
};
)
}

export default Minting;
export default Minting
Loading