Skip to content
Open
Changes from 1 commit
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
0ce5e68
Create readme.md
Mosamorphing Oct 15, 2024
9001155
Update readme.md
Mosamorphing Oct 15, 2024
968cdb0
Create next-init-page.png
Mosamorphing Oct 15, 2024
5a1d099
Add files via upload
Mosamorphing Oct 15, 2024
49b9984
Merge branch 'WTFAcademy:main' into main
Mosamorphing Oct 31, 2024
32a7327
Update readme.md
Mosamorphing Nov 7, 2024
c8c0e2e
Add files via upload
Mosamorphing Nov 7, 2024
6c821e0
Update readme.md
Mosamorphing Nov 7, 2024
b397f2a
Update readme.md
Mosamorphing Nov 7, 2024
28bb3ae
Add files via upload
Mosamorphing Nov 7, 2024
e69c433
Update readme.md
Mosamorphing Nov 7, 2024
695c590
Create Web3.tsx
Mosamorphing Nov 7, 2024
b201f12
Create Web3.tsx
Mosamorphing Nov 7, 2024
72286cd
Create readme.md
Mosamorphing Nov 7, 2024
57793c4
Create faucet.png
Mosamorphing Nov 7, 2024
c47196b
Add files via upload
Mosamorphing Nov 7, 2024
86c3159
Create readme.md
Mosamorphing Nov 7, 2024
42c7ea4
Create connect.png
Mosamorphing Nov 7, 2024
7ad6344
Add files via upload
Mosamorphing Nov 7, 2024
8688fd4
Create Web3.tsx
Mosamorphing Nov 7, 2024
e48759d
Create readme.md
Mosamorphing Nov 7, 2024
1719674
Create metamask.png
Mosamorphing Nov 7, 2024
dc8244c
Add files via upload
Mosamorphing Nov 7, 2024
3576a8c
Create readme.md
Mosamorphing Nov 7, 2024
7df8b7d
Create Web3.tsx
Mosamorphing Nov 7, 2024
4ca95b5
Create Web3.tsx
Mosamorphing Nov 7, 2024
0e1310b
Add files via upload
Mosamorphing Nov 7, 2024
6471fa4
Update readme.md
Mosamorphing Nov 7, 2024
801b2ea
Update readme.md
Mosamorphing Nov 7, 2024
a91bfdd
Update readme.md
Mosamorphing Nov 7, 2024
4fdacac
Update readme.md
Mosamorphing Nov 7, 2024
0e42342
Create readme.md
Mosamorphing Nov 7, 2024
59aa147
Update readme.md
Mosamorphing Nov 7, 2024
bca70dc
Create createnew.png
Mosamorphing Nov 7, 2024
576e25f
Add files via upload
Mosamorphing Nov 7, 2024
fd553fd
Update readme.md
Mosamorphing Nov 7, 2024
e956587
Create readme.md
Mosamorphing Nov 7, 2024
8b9e281
Create remix.png
Mosamorphing Nov 7, 2024
d5d50d7
Add files via upload
Mosamorphing Nov 7, 2024
aca0921
Update readme.md
Mosamorphing Nov 7, 2024
655a975
Update readme.md
Mosamorphing Nov 7, 2024
93709f8
Create MyToken.sol
Mosamorphing Nov 7, 2024
705cd61
Update MyToken.sol
Mosamorphing Nov 7, 2024
4a0db7d
Create readme.md
Mosamorphing Nov 8, 2024
f26ae02
Create connect1.png
Mosamorphing Nov 8, 2024
086b794
Add files via upload
Mosamorphing Nov 8, 2024
a442acc
Create Web3.tsx
Mosamorphing Nov 8, 2024
c213e51
Create readme.md
Mosamorphing Nov 8, 2024
cdf62a5
Create Web3.tsx
Mosamorphing Nov 8, 2024
8d419b2
Create demo.png
Mosamorphing Nov 8, 2024
6e10641
Add files via upload
Mosamorphing Nov 8, 2024
fdb6e7b
Create readme.md
Mosamorphing Nov 8, 2024
773127c
Create walletconnect.png
Mosamorphing Nov 8, 2024
d42c522
Add files via upload
Mosamorphing Nov 8, 2024
a6c3a51
Create Web3.tsx
Mosamorphing Nov 8, 2024
0c8eed4
Update readme.md
Mosamorphing Nov 8, 2024
812cd05
Create readme.md
Mosamorphing Nov 8, 2024
ab6b551
Create Web3.tsx
Mosamorphing Nov 8, 2024
8a17bd4
Create image.png
Mosamorphing Nov 8, 2024
4e7b65e
Add files via upload
Mosamorphing Nov 8, 2024
fd138c0
Create readme.md
Mosamorphing Nov 8, 2024
7e32b05
Create demo.png
Mosamorphing Nov 11, 2024
8dc6b77
Add files via upload
Mosamorphing Nov 11, 2024
85c92af
Create readme.md
Mosamorphing Nov 11, 2024
ec96757
Update readme.md
Mosamorphing Nov 11, 2024
da04bc5
Create send.png
Mosamorphing Nov 11, 2024
b542ecb
Add files via upload
Mosamorphing Nov 11, 2024
81dd076
Create readme.md
Mosamorphing Nov 11, 2024
06247a9
Create localnode.png
Mosamorphing Nov 11, 2024
5ba4c01
Add files via upload
Mosamorphing Nov 11, 2024
0bebb85
Create readme.md
Mosamorphing Nov 11, 2024
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
Prev Previous commit
Next Next commit
Create readme.md
  • Loading branch information
Mosamorphing authored Nov 8, 2024
commit fd138c0156c544c56a68c5763a7d0ba18d077275
269 changes: 269 additions & 0 deletions languages/en/12_Signature/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
Signature verification is a crucial process in ensuring secure transactions. In this lesson, we'll explore how to implement client-side signing and server-side verification.

---

## Why are Signatures Important?

In decentralized applications (DApps), user identities are typically tied to blockchain addresses, with each address representing a unique user. In traditional systems, we authenticate users using methods like passwords and SMS codes. But how do we confirm that someone is the rightful owner of a blockchain address in a DApp?

Previously, we demonstrated how to connect to a blockchain address by accessing the user's wallet, allowing the DApp to retrieve the address. However, just having access to an address doesn't confirm ownership. Should we permit users to manage related assets in the DApp simply because they've connected their address?

For assets that exist on the blockchain, this could work, as smart contract interactions require signing with the private key linked to the address. But not all assets are on-chain. If your DApp interacts with assets stored in a traditional database, you must verify that the user has the appropriate permissions.

Relying solely on wallet connections to verify ownership is unreliable because the process of retrieving an address can be deceived by client-side spoofing. Therefore, we require users to authenticate their identity by signing a message with their private key. The DApp's server then uses the corresponding public key to verify the signature, ensuring the user has the right permissions.

In this lesson, we'll create a simple example where you can sign a message and verify your identity after connecting to a wallet.

![demo](./img/demo.png)

### Implementing Frontend Signature

Let's start by implementing the frontend logic. Following the steps from the previous lesson, we will quickly set up [wallet connection](../03_ConnectWallet/readme.md).

Begin by creating a new file named `pages/sign/index.tsx`. Copy the relevant code from the previous example and modify it to build a new component called `components/SignDemo`.

This approach ensures continuity and builds on the existing framework, making it easier to integrate the new functionality.

```diff
import React from 'react';
- import { Address, ConnectButton, Connector, NFTCard } from "@ant-design/web3";
import { MetaMask, WagmiWeb3ConfigProvider } from "@ant-design/web3-wagmi";
import { createConfig, http } from 'wagmi';
import { injected } from "wagmi/connectors";
import { mainnet } from 'wagmi/chains';
+ import SignDemo from '../../components/SignDemo';


const config = createConfig({
chains: [mainnet],
transports: {
[mainnet.id]: http(),
},
connectors: [
injected({
target: "metaMask",
}),
],
});
const Demo:React.FC = () => {
return (
<WagmiWeb3ConfigProvider eip6963 config={config} wallets={[MetaMask()]}>
+ <SignDemo />
- <Address format address="0xEcd0D12E21805803f70de03B72B1C162dB0898d9" />
- <NFTCard
- address="0xEcd0D12E21805803f70de03B72B1C162dB0898d9"
- tokenId={641}
- />
- <Connector>
- <ConnectButton />
- </Connector>
</WagmiWeb3ConfigProvider>
);
}
export default Demo;

```
In the `SignDemo` component, you'll create a simple button for connecting a wallet. Below is the code for this button:

```tsx
import React from "react";
import { ConnectButton, Connector } from "@ant-design/web3";

const SignDemo: React.FC = () => {
return (
<Connector>
<ConnectButton />
</Connector>
);
};
export default SignDemo;
```
We have now established the fundamental connection logic.

Next, we will enhance the logic for the signature section. To do this, we first need to utilize the `useSignMessage` hook from `wagmi` and the `useAccount` hooks from Ant Design Web3 to implement the `doSignature` function.

```diff
import React from "react";
- import { ConnectButton, Connector } from "@ant-design/web3";
+ import { ConnectButton, Connector, useAccount } from "@ant-design/web3";
+ import { useSignMessage } from "wagmi";
+ import { message } from "antd";

const SignDemo: React.FC = () => {
+ const { signMessageAsync } = useSignMessage();
+ const { account } = useAccount();

+ const doSignature = async () => {
+ try {
+ const signature = await signMessageAsync({
+ message: "test message for WTF-DApp demo",
+ });
+ } catch (error: any) {
+ message.error(`Signature failed: ${error.message}`);
+ }
+ };

return (
<Connector>
<ConnectButton />
</Connector>
);
};
export default SignDemo;
```
Let's create a button that activates the `doSignature` method when clicked. To ensure functionality, we've configured the button's `disabled` attribute so that it only becomes active after a successful connection is established.

```diff
import React from "react";
import { ConnectButton, Connector, useAccount } from "@ant-design/web3";
import { useSignMessage } from "wagmi";
- import { message } from "antd";
+ import { message, Space, Button } from "antd";

const SignDemo: React.FC = () => {

// ...

return (
+ <Space>
<Connector>
<ConnectButton />
</Connector>
+ <Button
+ disabled={!account?.address}
+ onClick={doSignature}
+ >
+ Sign message
+ </Button>
+ </Space>
);
};
export default SignDemo;
```

To implement the client-side signature logic, we must also handle server-side verification. As previously discussed, signatures created on the client need to be verified by the server. Let's set up the server-side verification endpoint next.

## Setting Up Server-Side Signature Verification

For verifying signatures on the server, libraries like `viem` or `ethers` are commonly used. We'll start by creating a new file named `/pages/api/signatureCheck.ts`. In Next.js, any file within the `/api` directory is automatically treated as a server-side [Vercel Function](https://vercel.com/docs/functions/quickstart).

We'll use the `viem` library to implement the server-side signature verification:

```ts
// /pages/api/signatureCheck.ts
import type { NextApiRequest, NextApiResponse } from "next";
import { createPublicClient, http } from "viem";
import { mainnet } from "viem/chains";

export const publicClient = createPublicClient({
chain: mainnet,
transport: http(),
});

export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
try {
const body = req.body;
const valid = await publicClient.verifyMessage({
address: body.address,
message: "test message for WTF-DApp demo",
signature: body.signature,
});
res.status(200).json({ data: valid });
} catch (err: any) {
res.status(500).json({ error: err.message });
}
}
```

If you're proficient with `ethers`, you can use the following code:

```tsx
const verifyMessage = async (signerAddress, signature) => {
const recoveredAddress = ethers.utils.verifyMessage(
"test message for WTF-DApp demo",
signature
);
return recoveredAddress === signerAddress;
};
```

## Front-end API Call Signature Verification

To conclude, let's implement the logic needed to handle front-end API call signature verification. Simply copy the code provided below and paste it into the `SignDemo` component:

```tsx
const checkSignature = async (params: {
address?: string;
signature: string;
}) => {
try {
const response = await fetch("/api/signatureCheck", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(params),
});
const result = await response.json();
if (result.data) {
message.success("Signature success");
} else {
message.error("Signature failed");
}
} catch (error) {
message.error("An error occurred");
}
};
```

In the `doSignature` method, the next step is to call this function and incorporate a Loading state to indicate progress.

```diff
import React from "react";
import { ConnectButton, Connector, useAccount } from "@ant-design/web3";
import { useSignMessage } from "wagmi";
import { message, Space, Button } from "antd";

const SignDemo: React.FC = () => {
const { signMessageAsync } = useSignMessage();
const { account } = useAccount();
+ const [signLoading, setSignLoading] = React.useState(false);

const doSignature = async () => {
+ setSignLoading(true);
try {
const signature = await signMessageAsync({
message: "test message for WTF-DApp demo",
});
+ await checkSignature({
+ address: account?.address,
+ signature,
+ });
} catch (error: any) {
message.error(`Signature failed: ${error.message}`);
}
+ setSignLoading(false);
};

// checkSignature here

return (
<Space>
<Connector>
<ConnectButton />
</Connector>
<Button
+ loading={signLoading}
disabled={!account?.address}
onClick={doSignature}
>
Sign message
</Button>
</Space>
);
};
export default SignDemo;
```

To access all the relevant source files, the complete code is available in the [sign directory](../demo/pages/sign).