diff --git a/.eslintrc.js b/.eslintrc.js
index d970045e..694e34be 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -17,7 +17,7 @@ module.exports = {
overrides: [
{
- files: ['src/*.js'],
+ files: ['src/**/*.js'],
parserOptions: {
sourceType: 'module',
},
diff --git a/src/index.html b/src/index.html
index 5ac5f884..00ebb6ad 100644
--- a/src/index.html
+++ b/src/index.html
@@ -1334,6 +1334,57 @@
+
+
+
+
+
+ Sign Typed Data Variants
+
+
+
+
+
+
+
+
+
+ Result:
+
+
r:
+
s:
+
v:
+
+
+
+
+
diff --git a/src/index.js b/src/index.js
index 40108a23..d0c8f5ee 100644
--- a/src/index.js
+++ b/src/index.js
@@ -13,6 +13,11 @@ import {
walletConnect,
} from './connections';
import Constants from './constants.json';
+import {
+ EIP712Domain,
+ getPermitMsgParams,
+ MSG_PRIMARY_TYPE,
+} from './signatures/utils';
import {
ERC20_SAMPLE_CONTRACTS,
ERC721_SAMPLE_CONTRACTS,
@@ -259,6 +264,27 @@ const signPermitVerify = document.getElementById('signPermitVerify');
const signPermitVerifyResult = document.getElementById(
'signPermitVerifyResult',
);
+
+// Sign Typed Data Variants
+
+const signBlurOrder = document.getElementById('signBlurOrder');
+const signPermitSingle = document.getElementById('signPermitSingle');
+const signPermitBatch = document.getElementById('signPermitBatch');
+const signSeaportBulkOrder = document.getElementById('signSeaportBulkOrder');
+
+const signPermitVariantResult = document.getElementById(
+ 'signPermitVariantResult',
+);
+const signPermitVariantResultR = document.getElementById(
+ 'signPermitVariantResultR',
+);
+const signPermitVariantResultS = document.getElementById(
+ 'signPermitVariantResultS',
+);
+const signPermitVariantResultV = document.getElementById(
+ 'signPermitVariantResultV',
+);
+
const siwe = document.getElementById('siwe');
const siweResources = document.getElementById('siweResources');
const siweBadDomain = document.getElementById('siweBadDomain');
@@ -442,8 +468,12 @@ const allConnectedButtons = [
signTypedDataV4Verify,
signTypedDataV4Batch,
signTypedDataV4Queue,
+ signBlurOrder,
signPermit,
+ signPermitSingle,
+ signPermitBatch,
signPermitVerify,
+ signSeaportBulkOrder,
siwe,
siweResources,
siweBadDomain,
@@ -498,7 +528,11 @@ const initialConnectedButtons = [
signTypedDataV4,
signTypedDataV4Batch,
signTypedDataV4Queue,
+ signBlurOrder,
signPermit,
+ signPermitSingle,
+ signPermitBatch,
+ signSeaportBulkOrder,
siwe,
siweResources,
siweBadDomain,
@@ -2696,49 +2730,6 @@ const initializeFormElements = () => {
/**
* Sign Permit
*/
- const EIP712Domain = [
- { name: 'name', type: 'string' },
- { name: 'version', type: 'string' },
- { name: 'chainId', type: 'uint256' },
- { name: 'verifyingContract', type: 'address' },
- ];
-
- const Permit = [
- { name: 'owner', type: 'address' },
- { name: 'spender', type: 'address' },
- { name: 'value', type: 'uint256' },
- { name: 'nonce', type: 'uint256' },
- { name: 'deadline', type: 'uint256' },
- ];
-
- const permitMsgParamsDomain = {
- name: 'MyToken',
- version: '1',
- verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC',
- chainId: chainIdInt,
- };
-
- function getPermitMsgParams() {
- const from = accounts[0];
-
- const permit = {
- owner: from,
- spender: '0x5B38Da6a701c568545dCfcB03FcB875f56beddC4',
- value: 3000,
- nonce: 0,
- deadline: 50000000000,
- };
-
- return {
- types: {
- EIP712Domain,
- Permit,
- },
- primaryType: 'Permit',
- domain: permitMsgParamsDomain,
- message: permit,
- };
- }
async function getNFTMsgParams() {
return {
@@ -2780,7 +2771,13 @@ const initializeFormElements = () => {
signPermit.onclick = async () => {
const from = accounts[0];
- const msgParams = getPermitMsgParams();
+ const msgParams = getPermitMsgParams(
+ {
+ primaryType: MSG_PRIMARY_TYPE.PERMIT,
+ chainId: chainIdInt,
+ },
+ { fromAddress: from },
+ );
let sign;
let r;
@@ -2792,6 +2789,7 @@ const initializeFormElements = () => {
method: 'eth_signTypedData_v4',
params: [from, JSON.stringify(msgParams)],
});
+
const { _r, _s, _v } = splitSig(sign);
r = `0x${_r.toString('hex')}`;
s = `0x${_s.toString('hex')}`;
@@ -2808,12 +2806,65 @@ const initializeFormElements = () => {
}
};
+ async function requestSignTypedDataVariant(primaryType) {
+ const from = accounts[0];
+ const msgParams = getPermitMsgParams(
+ {
+ primaryType,
+ chainId: chainIdInt,
+ },
+ { fromAddress: from },
+ );
+
+ let sign;
+ let r;
+ let s;
+ let v;
+
+ try {
+ sign = await provider.request({
+ method: 'eth_signTypedData_v4',
+ params: [from, JSON.stringify(msgParams)],
+ });
+
+ const { _r, _s, _v } = splitSig(sign);
+ r = `0x${_r.toString('hex')}`;
+ s = `0x${_s.toString('hex')}`;
+ v = _v.toString();
+
+ signPermitVariantResult.innerHTML = sign;
+ signPermitVariantResultR.innerHTML = `r: ${r}`;
+ signPermitVariantResultS.innerHTML = `s: ${s}`;
+ signPermitVariantResultV.innerHTML = `v: ${v}`;
+ signPermitVerify.disabled = false;
+ } catch (err) {
+ console.error(err);
+ signPermitVariantResult.innerHTML = `Error: ${err.message}`;
+ }
+ }
+
+ signBlurOrder.onclick = async () => {
+ await requestSignTypedDataVariant(MSG_PRIMARY_TYPE.BLUR_ORDER);
+ };
+ signPermitBatch.onclick = async () => {
+ await requestSignTypedDataVariant(MSG_PRIMARY_TYPE.PERMIT_BATCH);
+ };
+ signPermitSingle.onclick = async () => {
+ await requestSignTypedDataVariant(MSG_PRIMARY_TYPE.PERMIT_SINGLE);
+ };
+ signSeaportBulkOrder.onclick = async () => {
+ await requestSignTypedDataVariant(MSG_PRIMARY_TYPE.SEAPORT_BULK_ORDER);
+ };
+
/**
* Sign Permit Verification
*/
signPermitVerify.onclick = async () => {
const from = accounts[0];
- const msgParams = getPermitMsgParams();
+ const msgParams = getPermitMsgParams({
+ primaryType: MSG_PRIMARY_TYPE.PERMIT,
+ chainId: chainIdInt,
+ });
try {
const sign = signPermitResult.innerHTML;
diff --git a/src/signatures/utils.js b/src/signatures/utils.js
new file mode 100644
index 00000000..3da17eb7
--- /dev/null
+++ b/src/signatures/utils.js
@@ -0,0 +1,453 @@
+export const EIP712Domain = [
+ { name: 'name', type: 'string' },
+ { name: 'version', type: 'string' },
+ { name: 'chainId', type: 'uint256' },
+ { name: 'verifyingContract', type: 'address' },
+];
+
+export const MSG_PRIMARY_TYPE = {
+ BLUR_ORDER: 'Order',
+ BLUR_ROOT: 'Root',
+ PERMIT: 'Permit',
+ PERMIT_BATCH: 'PermitBatch',
+ PERMIT_SINGLE: 'PermitSingle',
+ SEAPORT_BULK_ORDER: 'BulkOrder',
+};
+
+export function getPermitMsgParams(
+ { chainId, primaryType },
+ { fromAddress } = {},
+) {
+ switch (primaryType) {
+ case MSG_PRIMARY_TYPE.BLUR_ORDER: {
+ return {
+ primaryType: 'Order',
+ types: {
+ EIP712Domain: [
+ { name: 'name', type: 'string' },
+ { name: 'version', type: 'string' },
+ { name: 'chainId', type: 'uint256' },
+ { name: 'verifyingContract', type: 'address' },
+ ],
+ MakerFee: [
+ { name: 'recipient', type: 'address' }, // Maker fee recipient address
+ { name: 'rate', type: 'uint256' }, // Maker fee rate
+ ],
+ Order: [
+ { name: 'assetType', type: 'uint8' }, // Asset type
+ { name: 'collection', type: 'address' }, // NFT collection address
+ { name: 'expirationTime', type: 'uint256' }, // Order expiration time in seconds
+ { name: 'listingsRoot', type: 'bytes32' }, // Listings root hash
+ { name: 'makerFee', type: 'MakerFee' }, // Maker fee
+ { name: 'nonce', type: 'uint256' }, // Nonce for order uniqueness
+ { name: 'numberOfListings', type: 'uint256' }, // Number of listings
+ { name: 'orderType', type: 'uint8' }, // Order type
+ { name: 'salt', type: 'uint256' }, // Salt for order uniqueness
+ { name: 'trader', type: 'address' }, // User address
+ ],
+ },
+ domain: {
+ name: 'Blur Exchange',
+ version: '1.0',
+ chainId,
+ verifyingContract: '0xb2ecfe4e4d61f8790bbb9de2d1259b9e2410cea5',
+ },
+ message: {
+ assetType: '0',
+ collection: '0x8a90cab2b38dba80c64b7734e58ee1db38b8992e',
+ expirationTime: '1739484503',
+ listingsRoot:
+ '0xf5126e36e0cf3f8ccdf8e3d76c1501c706c15a029fb8139bca7b536776e9eafe',
+ makerFee: {
+ recipient: '0x0000000000000000000000000000000000000000',
+ rate: '0',
+ },
+ nonce: '0',
+ numberOfListings: '1',
+ orderType: '1',
+ salt: '106171581059276559763059578085820161781',
+ trader: fromAddress,
+ },
+ };
+ }
+ case MSG_PRIMARY_TYPE.PERMIT: {
+ return {
+ primaryType: 'Permit',
+ types: {
+ EIP712Domain,
+ Permit: [
+ { name: 'owner', type: 'address' },
+ { name: 'spender', type: 'address' },
+ { name: 'value', type: 'uint256' },
+ { name: 'nonce', type: 'uint256' },
+ { name: 'deadline', type: 'uint256' },
+ ],
+ },
+ domain: {
+ chainId,
+ name: 'MyToken',
+ verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC',
+ version: '1',
+ },
+ message: {
+ owner: fromAddress,
+ spender: '0x5B38Da6a701c568545dCfcB03FcB875f56beddC4',
+ value: 3000,
+ nonce: 0,
+ deadline: 50000000000,
+ },
+ };
+ }
+ case MSG_PRIMARY_TYPE.PERMIT_BATCH: {
+ return {
+ primaryType: 'PermitBatch',
+ types: {
+ PermitBatch: [
+ {
+ name: 'details',
+ type: 'PermitDetails[]',
+ },
+ {
+ name: 'spender',
+ type: 'address',
+ },
+ {
+ name: 'sigDeadline',
+ type: 'uint256',
+ },
+ ],
+ PermitDetails: [
+ {
+ name: 'token',
+ type: 'address',
+ },
+ {
+ name: 'amount',
+ type: 'uint160',
+ },
+ {
+ name: 'expiration',
+ type: 'uint48',
+ },
+ {
+ name: 'nonce',
+ type: 'uint48',
+ },
+ ],
+ EIP712Domain: [
+ {
+ name: 'name',
+ type: 'string',
+ },
+ {
+ name: 'chainId',
+ type: 'uint256',
+ },
+ {
+ name: 'verifyingContract',
+ type: 'address',
+ },
+ ],
+ },
+ domain: {
+ chainId,
+ name: 'Permit2',
+ verifyingContract: '0x000000000022d473030f116ddee9f6b43ac78ba3',
+ version: '1',
+ },
+ message: {
+ details: [
+ {
+ token: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
+ amount: '1461501637330902918203684832716283019655932542975',
+ expiration: '1722887542',
+ nonce: '5',
+ },
+ {
+ token: '0xb0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
+ amount: '98765432109876543210',
+ expiration: '1722887642',
+ nonce: '6',
+ },
+ ],
+ spender: '0x3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad',
+ sigDeadline: '1720297342',
+ },
+ };
+ }
+ case MSG_PRIMARY_TYPE.PERMIT_SINGLE: {
+ return {
+ primaryType: 'PermitSingle',
+ types: {
+ PermitSingle: [
+ {
+ name: 'details',
+ type: 'PermitDetails',
+ },
+ {
+ name: 'spender',
+ type: 'address',
+ },
+ {
+ name: 'sigDeadline',
+ type: 'uint256',
+ },
+ ],
+ PermitDetails: [
+ {
+ name: 'token',
+ type: 'address',
+ },
+ {
+ name: 'amount',
+ type: 'uint160',
+ },
+ {
+ name: 'expiration',
+ type: 'uint48',
+ },
+ {
+ name: 'nonce',
+ type: 'uint48',
+ },
+ ],
+ EIP712Domain,
+ },
+ domain: {
+ chainId,
+ name: 'Permit2',
+ verifyingContract: '0x000000000022d473030f116ddee9f6b43ac78ba3',
+ version: '1',
+ },
+ message: {
+ details: {
+ token: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
+ amount: '1461501637330902918203684832716283019655932542975',
+ expiration: '1722887542',
+ nonce: '5',
+ },
+ spender: '0x3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad',
+ sigDeadline: '1720297342',
+ },
+ };
+ }
+ case MSG_PRIMARY_TYPE.SEAPORT_BULK_ORDER: {
+ return {
+ primaryType: 'BulkOrder',
+ types: {
+ BulkOrder: [{ name: 'tree', type: 'OrderComponents[][]' }],
+ OrderComponents: [
+ { name: 'offerer', type: 'address' },
+ { name: 'zone', type: 'address' },
+ { name: 'offer', type: 'OfferItem[]' },
+ { name: 'consideration', type: 'ConsiderationItem[]' },
+ { name: 'orderType', type: 'uint8' },
+ { name: 'startTime', type: 'uint256' },
+ { name: 'endTime', type: 'uint256' },
+ { name: 'zoneHash', type: 'bytes32' },
+ { name: 'salt', type: 'uint256' },
+ { name: 'conduitKey', type: 'bytes32' },
+ { name: 'counter', type: 'uint256' },
+ ],
+ OfferItem: [
+ { name: 'itemType', type: 'uint8' },
+ { name: 'token', type: 'address' },
+ { name: 'identifierOrCriteria', type: 'uint256' },
+ { name: 'startAmount', type: 'uint256' },
+ { name: 'endAmount', type: 'uint256' },
+ ],
+ ConsiderationItem: [
+ { name: 'itemType', type: 'uint8' },
+ { name: 'token', type: 'address' },
+ { name: 'identifierOrCriteria', type: 'uint256' },
+ { name: 'startAmount', type: 'uint256' },
+ { name: 'endAmount', type: 'uint256' },
+ { name: 'recipient', type: 'address' },
+ ],
+ },
+ domain: {
+ name: 'Seaport',
+ version: '1.5',
+ chainId,
+ verifyingContract: '0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC',
+ },
+ message: {
+ tree: [
+ [
+ {
+ offerer: fromAddress,
+ offer: [
+ {
+ itemType: '2',
+ token: '0xafd4896984CA60d2feF66136e57f958dCe9482d5',
+ identifierOrCriteria: '2104',
+ startAmount: '1',
+ endAmount: '1',
+ },
+ ],
+ consideration: [
+ {
+ itemType: '0',
+ token: '0x0000000000000000000000000000000000000000',
+ identifierOrCriteria: '0',
+ startAmount: '900000000000000000',
+ endAmount: '900000000000000000',
+ recipient: fromAddress,
+ },
+ {
+ itemType: '0',
+ token: '0x0000000000000000000000000000000000000000',
+ identifierOrCriteria: '0',
+ startAmount: '25000000000000000',
+ endAmount: '25000000000000000',
+ recipient: '0x0000a26b00c1F0DF003000390027140000fAa719',
+ },
+ {
+ itemType: '0',
+ token: '0x0000000000000000000000000000000000000000',
+ identifierOrCriteria: '0',
+ startAmount: '75000000000000000',
+ endAmount: '75000000000000000',
+ recipient: '0x839221C3F9dce0ef5318356c447F8192eD04d38C',
+ },
+ ],
+ startTime: '1705733618',
+ endTime: '1708411897',
+ orderType: '0',
+ zone: '0x004C00500000aD104D7DBd00e3ae0A5C00560C00',
+ zoneHash:
+ '0x0000000000000000000000000000000000000000000000000000000000000000',
+ salt: '24446860302761739304752683030156737591518664810215442929806792077947493999137',
+ conduitKey:
+ '0x0000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f0000',
+ totalOriginalConsiderationItems: '3',
+ counter: '0',
+ },
+ {
+ offerer: fromAddress,
+ offer: [
+ {
+ itemType: '2',
+ token: '0xafd4896984CA60d2feF66136e57f958dCe9482d5',
+ identifierOrCriteria: '2103',
+ startAmount: '1',
+ endAmount: '1',
+ },
+ ],
+ consideration: [
+ {
+ itemType: '0',
+ token: '0x0000000000000000000000000000000000000000',
+ identifierOrCriteria: '0',
+ startAmount: '900000000000000000',
+ endAmount: '900000000000000000',
+ recipient: fromAddress,
+ },
+ {
+ itemType: '0',
+ token: '0x0000000000000000000000000000000000000000',
+ identifierOrCriteria: '0',
+ startAmount: '25000000000000000',
+ endAmount: '25000000000000000',
+ recipient: '0x0000a26b00c1F0DF003000390027140000fAa719',
+ },
+ {
+ itemType: '0',
+ token: '0x0000000000000000000000000000000000000000',
+ identifierOrCriteria: '0',
+ startAmount: '75000000000000000',
+ endAmount: '75000000000000000',
+ recipient: '0x839221C3F9dce0ef5318356c447F8192eD04d38C',
+ },
+ ],
+ startTime: '1705733618',
+ endTime: '1708411897',
+ orderType: '0',
+ zone: '0x004C00500000aD104D7DBd00e3ae0A5C00560C00',
+ zoneHash:
+ '0x0000000000000000000000000000000000000000000000000000000000000000',
+ salt: '24446860302761739304752683030156737591518664810215442929800482629144425056459',
+ conduitKey:
+ '0x0000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f0000',
+ totalOriginalConsiderationItems: '3',
+ counter: '0',
+ },
+ ],
+ [
+ {
+ offerer: fromAddress,
+ offer: [
+ {
+ itemType: '2',
+ token: '0xafd4896984CA60d2feF66136e57f958dCe9482d5',
+ identifierOrCriteria: '2102',
+ startAmount: '1',
+ endAmount: '1',
+ },
+ ],
+ consideration: [
+ {
+ itemType: '0',
+ token: '0x0000000000000000000000000000000000000000',
+ identifierOrCriteria: '0',
+ startAmount: '900000000000000000',
+ endAmount: '900000000000000000',
+ recipient: fromAddress,
+ },
+ {
+ itemType: '0',
+ token: '0x0000000000000000000000000000000000000000',
+ identifierOrCriteria: '0',
+ startAmount: '25000000000000000',
+ endAmount: '25000000000000000',
+ recipient: '0x0000a26b00c1F0DF003000390027140000fAa719',
+ },
+ {
+ itemType: '0',
+ token: '0x0000000000000000000000000000000000000000',
+ identifierOrCriteria: '0',
+ startAmount: '75000000000000000',
+ endAmount: '75000000000000000',
+ recipient: '0x839221C3F9dce0ef5318356c447F8192eD04d38C',
+ },
+ ],
+ startTime: '1705733618',
+ endTime: '1708411897',
+ orderType: '0',
+ zone: '0x004C00500000aD104D7DBd00e3ae0A5C00560C00',
+ zoneHash:
+ '0x0000000000000000000000000000000000000000000000000000000000000000',
+ salt: '24446860302761739304752683030156737591518664810215442929800836178870704788113',
+ conduitKey:
+ '0x0000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f0000',
+ totalOriginalConsiderationItems: '3',
+ counter: '0',
+ },
+ {
+ offerer: '0x0000000000000000000000000000000000000000',
+ zone: '0x0000000000000000000000000000000000000000',
+ offer: [],
+ consideration: [],
+ orderType: '0',
+ startTime: '0',
+ endTime: '0',
+ zoneHash:
+ '0x0000000000000000000000000000000000000000000000000000000000000000',
+ salt: '0',
+ conduitKey:
+ '0x0000000000000000000000000000000000000000000000000000000000000000',
+ counter: '0',
+ totalOriginalConsiderationItems: '0',
+ },
+ ],
+ ],
+ },
+ };
+ }
+ default: {
+ throw new Error(
+ `Unknown Sign Typed Signature primary type: ${primaryType}`,
+ );
+ }
+ }
+}