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}`, + ); + } + } +}