Skip to content

Commit

Permalink
script: add opensea login isValidSignature example
Browse files Browse the repository at this point in the history
  • Loading branch information
kbehouse committed Jun 10, 2023
1 parent 3c22247 commit 99a3f5b
Show file tree
Hide file tree
Showing 5 changed files with 1,375 additions and 1,042 deletions.
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@
"deploy-bloctoaccountfactory": "hardhat run deploy/2_deploy_BloctoAccountFactory.ts",
"deploy-verifyingpaymaster": "hardhat run deploy/3_deploy_VerifyingPaymaster.ts",
"verify-bloctoaccountcloneable": "npx hardhat verify --contract contracts/BloctoAccountCloneableWallet.sol:BloctoAccountCloneableWallet 0x66d4d44e4957F70dF0127b3de2dBaD9fF9058B5B 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
"verify-accountfactory": "npx hardhat verify 0xC261555D0e4623BF709d3f22Bf58A6275CE4d17D 0x66d4d44e4957F70dF0127b3de2dBaD9fF9058B5B 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
"verify-bloctoaccountfactory": "npx hardhat verify 0xC261555D0e4623BF709d3f22Bf58A6275CE4d17D 0x66d4d44e4957F70dF0127b3de2dBaD9fF9058B5B 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
"verify-verifyingpaymaster": "npx hardhat verify 0xE671dEee9c758e642d50c32e13FD5fC6D42C98F1 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789 0x086443C6bA8165a684F3e316Da42D3A2F0a2330a"
},
"devDependencies": {
"@account-abstraction/contracts": "^0.6.0",
"@ethereumjs/util": "^8.0.6",
"@nomiclabs/hardhat-ethers": "^2.0.2",
"@nomiclabs/hardhat-waffle": "^2.0.1",
"@openzeppelin/hardhat-upgrades": "^1.23.0",
Expand All @@ -38,6 +39,7 @@
"eslint-plugin-standard": "^5.0.0",
"ethereum-waffle": "^3.4.0",
"ethers": "^5.4.2",
"ethjs-util": "^0.1.6",
"hardhat": "^2.6.6",
"hardhat-storage-layout": "^0.1.7",
"solhint": "^3.3.7",
Expand All @@ -63,4 +65,4 @@
"table": "^6.8.0",
"typescript": "^4.3.5"
}
}
}
67 changes: 67 additions & 0 deletions scripts/opensea_isValidSignature.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// NOTE: this file includes keys, SHOULD delete it before publish to public repo
import { ethers } from 'hardhat'
import { Wallet } from 'ethers'
import {
hashMessageEIP191V0
} from '../test/testutils'

import {
utilHashPersonalMessage,
concatSig
} from '../test/utils'

import { toBuffer } from '@ethereumjs/util'

const ERC1271_MAGICVALUE_BYTES32 = '0x1626ba7e'
// const accountAddr = '0x604d34155b6e5bb63bf5b08ee50cf67b9ceef6ab'
const accountAddr = '0xFaf346Bff9f53bF0ef8B85391694738a13CF1D8c'

// SHOULD delete following key before publish to public repo
// authorized key
const AKey = '13a2f40830e4edbef23ff7b7d4d94b7357207883c6349d10e6a6225c4bc6bb73'
// cosigner key
const CKey = 'ba85ec51d96206e305fee5f3e61dbebfa4d4065df0d677ab998f8fe636d63e4e'

const msg = 'Welcome to OpenSea!\n\nClick to sign in and accept the OpenSea Terms of Service (https://opensea.io/tos) and Privacy Policy (https://opensea.io/privacy).\n\nThis request will not trigger a blockchain transaction or cost any gas fees.\n\nYour authentication status will reset after 24 hours.\n\nWallet address:\n0xfaf346bff9f53bf0ef8b85391694738a13cf1d8c\n\nNonce:\nd712ae2f-e004-4acd-ae4f-f5b65d54858a'

function sign (wallet: Wallet, hashPersonalMsg: string): string {
console.log(hashMessageEIP191V0(accountAddr, hashPersonalMsg))
const sig = wallet._signingKey().signDigest(hashMessageEIP191V0(accountAddr, hashPersonalMsg))

const serializedSig = concatSig(toBuffer(sig.v), toBuffer(sig.r), toBuffer(sig.s))
return serializedSig
}

async function main (): Promise<void> {
const Account = await ethers.getContractFactory('BloctoAccount')
const account = await Account.attach(accountAddr)

console.log('accountAddr: ', accountAddr)

const authorizedWallet = new ethers.Wallet(AKey)
console.log('authorized(device) address: ', authorizedWallet.address)
const cosignerWallet = new ethers.Wallet(CKey)
console.log('cosigner address: ', cosignerWallet.address)

const hashPersonalMsg = utilHashPersonalMessage(msg)
console.log('hashPersonalMsg:', hashPersonalMsg)
const authorizedSig = sign(authorizedWallet, hashPersonalMsg)
const cosignerSig = sign(cosignerWallet, hashPersonalMsg)
console.log('cosignerSig: ', cosignerSig)
const combinedSig = authorizedSig + cosignerSig.slice(2)
// console.log(sig)
console.log('combinedSig: ', combinedSig)
const result = await account.isValidSignature(hashPersonalMsg, combinedSig)
console.log('result: ', result)

if (ERC1271_MAGICVALUE_BYTES32 === result) {
console.log('Valid signature success!')
}
}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
console.error(error)
process.exitCode = 1
})
10 changes: 0 additions & 10 deletions test/testutils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,18 +299,8 @@ export const txData = (revert: number, to: string, amount: BigNumber, dataBuff:

export const EIP191V0MessagePrefix = '\x19\x00'
export function hashMessageEIP191V0 (address: string, message: Bytes | string): string {
if (typeof (message) === 'string') {
message = toUtf8Bytes(message)
}
address = address.replace('0x', '')

// const tx = concat([
// toUtf8Bytes(EIP191V0MessagePrefix),
// Uint8Array.from(Buffer.from(address, 'hex')),
// message
// ])
// console.log('hashMessageEIP191V0 tx', tx)

return keccak256(concat([
toUtf8Bytes(EIP191V0MessagePrefix),
Uint8Array.from(Buffer.from(address, 'hex')),
Expand Down
153 changes: 153 additions & 0 deletions test/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// fork from: https://github.com/MetaMask/eth-sig-util/blob/main/src/utils.ts
import {
addHexPrefix,
bufferToHex,
bufferToInt,
ecrecover,
fromRpcSig,
fromSigned,
toBuffer,
ToBufferInputTypes,
toUnsigned,
ecsign,
hashPersonalMessage
} from '@ethereumjs/util'
import { intToHex, isHexString, stripHexPrefix } from 'ethjs-util'

/**
* Pads the front of the given hex string with zeroes until it reaches the
* target length. If the input string is already longer than or equal to the
* target length, it is returned unmodified.
*
* If the input string is "0x"-prefixed or not a hex string, an error will be
* thrown.
*
* @param hexString - The hexadecimal string to pad with zeroes.
* @param targetLength - The target length of the hexadecimal string.
* @returns The input string front-padded with zeroes, or the original string
* if it was already greater than or equal to to the target length.
*/
export function padWithZeroes (hexString: string, targetLength: number): string {
if (hexString !== '' && !/^[a-f0-9]+$/iu.test(hexString)) {
throw new Error(
`Expected an unprefixed hex string. Received: ${hexString}`
)
}

if (targetLength < 0) {
throw new Error(
`Expected a non-negative integer target length. Received: ${targetLength}`
)
}

return String.prototype.padStart.call(hexString, targetLength, '0')
}

/**
* Returns `true` if the given value is nullish.
*
* @param value - The value being checked.
* @returns Whether the value is nullish.
*/
export function isNullish (value) {
return value === null || value === undefined
}

/**
* Convert a value to a Buffer. This function should be equivalent to the `toBuffer` function in
* `[email protected]`.
*
* @param value - The value to convert to a Buffer.
* @returns The given value as a Buffer.
*/
export function legacyToBuffer (value: ToBufferInputTypes) {
return typeof value === 'string' && !isHexString(value)
? Buffer.from(value)
: toBuffer(value)
}

/**
* Concatenate an extended ECDSA signature into a single '0x'-prefixed hex string.
*
* @param v - The 'v' portion of the signature.
* @param r - The 'r' portion of the signature.
* @param s - The 's' portion of the signature.
* @returns The concatenated ECDSA signature as a '0x'-prefixed string.
*/
export function concatSig (v: Buffer, r: Buffer, s: Buffer): string {
const rSig = fromSigned(r)
const sSig = fromSigned(s)
const vSig = bufferToInt(v)
const rStr = padWithZeroes(toUnsigned(rSig).toString('hex'), 64)
const sStr = padWithZeroes(toUnsigned(sSig).toString('hex'), 64)
const vStr = stripHexPrefix(intToHex(vSig))
return addHexPrefix(rStr.concat(sStr, vStr))
}

/**
* Recover the public key from the given signature and message hash.
*
* @param messageHash - The hash of the signed message.
* @param signature - The signature.
* @returns The public key of the signer.
*/
export function recoverPublicKey (
messageHash: Buffer,
signature: string
): Buffer {
const sigParams = fromRpcSig(signature)
return ecrecover(messageHash, sigParams.v, sigParams.r, sigParams.s)
}

/**
* Normalize the input to a lower-cased '0x'-prefixed hex string.
*
* @param input - The value to normalize.
* @returns The normalized value.
*/
export function normalize (input: number | string): string | undefined {
if (!input) {
return undefined
}

if (typeof input === 'number') {
if (input < 0) {
return '0x'
}
const buffer = toBuffer(input)
input = bufferToHex(buffer)
}

if (typeof input !== 'string') {
let msg = 'eth-sig-util.normalize() requires hex string or integer input.'
msg += ` received ${typeof input}: ${input as any as string}`
throw new Error(msg)
}

return addHexPrefix(input.toLowerCase())
}

/**
* Node's Buffer.from() method does not seem to buffer numbers correctly out of the box.
* This helper method formats the number correct for Buffer.from to return correct buffer.
*
* @param num - The number to convert to buffer.
* @returns The number in buffer form.
*/
export function numberToBuffer (num: number) {
const hexVal = num.toString(16)
const prepend = hexVal.length % 2 ? '0' : ''
return Buffer.from(prepend + hexVal, 'hex')
}

export function utilHashPersonalMessage (
data: ToBufferInputTypes
): string {
if (isNullish(data)) {
throw new Error('Missing data parameter')
}

const message = legacyToBuffer(data)
const msgHash = hashPersonalMessage(message)
return bufferToHex(msgHash)
}
Loading

0 comments on commit 99a3f5b

Please sign in to comment.