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:
${domain} wants you to sign in with your ${blockchain} account:
${address}
${statement}
URI: ${uri}
Version: ${version}
Chain ID: ${chain-id}
Nonce: ${nonce}
Issued At: ${issued-at}
Expiration Time: ${expiration-time}
Not Before: ${not-before}
Request ID: ${request-id}
Resources:
- ${resources[0]}
- ${resources[1]}
...
- ${resources[n]}
| Name | Type | Required? | Description |
|---|
domain | string | true | The authority that is requesting the signing. |
address | string | true | The blockchain address that is performing the signing. |
statement | string | false | A human-readable ASCII assertion that the user will sign. It MUST NOT contain \\n. |
uri | string | true | A URI referring to the resource that is the subject of the signing—that is, the subject of the claim. |
version | string | true | The current version of the message. |
chain-id | string | true | The Chain ID to which the session is bound, and the network where Contract Accounts MUST be resolved. |
nonce | string | true | A randomized token to prevent signature replay attacks. |
issued-at | string | true | The issuance time. |
expiration-time | string | false | The time at which the signed authentication message is no longer valid. |
not-before | string | false | The time at which the signed authentication message starts being valid. |
request-id | string | false | A system-specific identifier used to uniquely refer to the authentication request. |
resources | string[] | false | A list of URIs the user wishes to have resolved as part of the authentication by the relying party. |
Sign-In with Solana
See our specification and integration guide on GitHub.
Sign-In with Ethereum
The Sign-In with Ethereum standard is defined by EIP-4361.
Examples
signMessage()
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");
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.request({
method: "signMessage",
params: {
message: encodedMessage,
display: "utf8",
}
});
Sign-In with X
The Sign-In with X standard is defined by CAIP-122. It uses CAIP-10 identifiers for the address field and CAIP-2 for chain-id.
While CAIP-122 is technically chain-agnostic, only Ethereum and Solana parsing are supported at this time.
Ethereum example
signMessage()
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");
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.request({
method: "signMessage",
params: {
message: encodedMessage,
display: "utf8",
}
});
Solana example
signMessage()
const provider = getProvider(); // see "Detecting the Provider"
const message = `magiceden.io wants you to sign in with your Solana account:
solana:mainnet:FYpB58cLw5cwiN763ayB2sFT8HLF2MRUBbbyRgHYiRpK
Click Sign or Approve only means you have proved this wallet is owned by you.
URI: https://magiceden.io
Version: 1
Chain ID: solana:mainnet
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");
request()
const provider = getProvider(); // see "Detecting the Provider"
const message = `magiceden.io wants you to sign in with your Solana account:
solana:mainnet:FYpB58cLw5cwiN763ayB2sFT8HLF2MRUBbbyRgHYiRpK
Click Sign or Approve only means you have proved this wallet is owned by you.
URI: https://magiceden.io
Version: 1
Chain ID: solana:mainnet
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",
}
});
Phantom uses Ed25519 signatures for Solana message signatures. To verify a message signature, you can use the tweetnacl npm package.