Signing a Message
When a web application is connected to Phantom, it can also request that the user signs a given message. Applications are free to write their own messages which will be displayed to users from within Phantom's signature prompt. Message signatures do not involve network fees and are a convenient way for apps to verify ownership of an address. You can see our handleSignMessage implementation to see how you can use libraries such as ethers.js to abstract away some of these intricacies
const message = 'To avoid digital dognappers, sign below to authenticate with CryptoCorgis.';
const from = accounts[0];
const msg = `0x${Buffer.from(message, 'utf8').toString('hex')}`;
const sign = await provider.request({
method: 'personal_sign',
params: [msg, from, 'Example password'],
});
Applications that rely on
signMessage
for authenticating users can choose to opt-in to one of the various Sign In With (SIW) standards. If a message follows one of the supported standards, Phantom will verify required fields at the time of signing. At the time of this writing, Phantom supports:
The serialized format of SIW messages is as follows:
Name | Type | Required? | Description |
---|---|---|---|
domain | string | The authority that is requesting the signing. | |
address | string | The blockchain address that is performing the signing. | |
statement | string | A human-readable ASCII assertion that the user will sign. It MUST NOT contain \n . | |
uri | string | A URI referring to the resource that is the subject of the signing (i.e. the subject of the claim). | |
version | string | The current version of the message. | |
chain-id | string | The Chain ID to which the session is bound, and the network where Contract Accounts MUST be resolved. | |
nonce | string | A randomized token to prevent signature replay attacks. | |
issued-at | string | The issuance time. | |
expiration-time | string | The time at which the signed authentication message is no longer valid. | |
not-before | string | The time at which the signed authentication message starts being valid. | |
request-id | string | A system-specific identifier used to uniquely refer to the authentication request. | |
resources | string[] | A list of uris the user wishes to have resolved as part of the authentication by the relying party. |
While CAIP-122 is technically chain-agnostic, only Ethereum and Solana parsing are supported at this time.
signMessage()
request()
const provider = getProvider(); // see "Detecting the Provider"
const message = `magiceden.io wants you to sign in with your Ethereum account:
eip155:1:0xb9c5714089478a327f09197987f16f9e5d936e8a
Click Sign or Approve only means you have proved this wallet is owned by you.
URI: https://magiceden.io
Version: 1
Chain ID: eip155:1
Nonce: bZQJ0SL6gJ
Issued At: 2022-10-25T16:52:02.748Z
Resources:
- https://foo.com
- https://bar.com`;
const encodedMessage = new TextEncoder().encode(message);
const signedMessage = await provider.signMessage(encodedMessage, "utf8");
const provider = getProvider(); // see "Detecting the Provider"
const message = `magiceden.io wants you to sign in with your Ethereum account:
eip155:1:0xb9c5714089478a327f09197987f16f9e5d936e8a
Click Sign or Approve only means you have proved this wallet is owned by you.
URI: https://magiceden.io
Version: 1
Chain ID: eip155:1
Nonce: bZQJ0SL6gJ
Issued At: 2022-10-25T16:52:02.748Z
Resources:
- https://foo.com
- https://bar.com`;
const encodedMessage = new TextEncoder().encode(message);
const signedMessage = await provider.request({
method: "signMessage",
params: {
message: encodedMessage,
display: "utf8",
});
signMessage()
request()
const provider = getProvider(); // see "Detecting the Provider"
const message = `magiceden.io wants you to sign in with your Ethereum account:
0xb9c5714089478a327f09197987f16f9e5d936e8a
Click Sign or Approve only means you have proved this wallet is owned by you.
URI: https://magiceden.io
Version: 1
Chain ID: 1
Nonce: bZQJ0SL6gJ
Issued At: 2022-10-25T16:52:02.748Z
Resources:
- https://foo.com
- https://bar.com`;
const encodedMessage = new TextEncoder().encode(message);
const signedMessage = await provider.signMessage(encodedMessage, "utf8");
const provider = getProvider(); // see "Detecting the Provider"
const message = `magiceden.io wants you to sign in with your Ethereum account:
0xb9c5714089478a327f09197987f16f9e5d936e8a
Click Sign or Approve only means you have proved this wallet is owned by you.
URI: https://magiceden.io
Version: 1
Chain ID: 1
Nonce: bZQJ0SL6gJ
Issued At: 2022-10-25T16:52:02.748Z
Resources:
- https://foo.com
- https://bar.com`;
const encodedMessage = new TextEncoder().encode(message);
const signedMessage = await provider.request({
method: "signMessage",
params
message: encodedMessage,
display: "utf8",
});