-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
script: add opensea login isValidSignature example
- Loading branch information
Showing
5 changed files
with
1,375 additions
and
1,042 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
Oops, something went wrong.