Skip to content

Commit

Permalink
Merge pull request #52 from consenlabs/feature/add_eip1271_eip712_sig…
Browse files Browse the repository at this point in the history
…ning_method

Add standard EIP712 + ERC1271 signing method
  • Loading branch information
BenjaminLu authored Feb 23, 2023
2 parents 76c0580 + 4de8c83 commit b1b30fd
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 14 deletions.
91 changes: 85 additions & 6 deletions src/signer/rfqv1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export async function signByMMPSigner(
])
signature = '0x' + signatureBuffer.toString('hex')
return signature
} else if (walletType === WalletType.ERC1271) {
} else if (walletType === WalletType.ERC1271_EIP712_EIP191) {
// | 32 byte | 32 byte |1 byte| 1 bytes |
// +---------|---------|------|---------+
// | R | S | V | type(5) |
Expand All @@ -91,9 +91,62 @@ export const forwardUnsignedOrder = async (signingUrl: string, orderInfo: any):
}
}

export const signRFQTx = async (
chainId: number,
rfqAddr: string,
signedOrder: any,
user: Wallet,
receiverAddr: string,
signatureType = SignatureType.EIP712
) => {
const domain = {
name: 'Tokenlon',
version: 'v5',
chainId: chainId,
verifyingContract: rfqAddr,
}

const types = {
fillWithPermit: [
{ name: 'makerAddr', type: 'address' },
{ name: 'takerAssetAddr', type: 'address' },
{ name: 'makerAssetAddr', type: 'address' },
{ name: 'takerAssetAmount', type: 'uint256' },
{ name: 'makerAssetAmount', type: 'uint256' },
{ name: 'takerAddr', type: 'address' },
{ name: 'receiverAddr', type: 'address' },
{ name: 'salt', type: 'uint256' },
{ name: 'deadline', type: 'uint256' },
{ name: 'feeFactor', type: 'uint256' },
],
}

// The data to sign
const value = {
makerAddr: signedOrder.makerAddr,
takerAssetAddr: signedOrder.takerAssetAddr,
makerAssetAddr: signedOrder.makerAssetAddr,
takerAssetAmount: signedOrder.takerAssetAmount.toString(),
makerAssetAmount: signedOrder.makerAssetAmount.toString(),
takerAddr: signedOrder.takerAddr,
receiverAddr: receiverAddr,
salt: signedOrder.salt.toString(),
deadline: signedOrder.deadline.toString(),
feeFactor: signedOrder.feeFactor.toString(),
}

const signatureTypedData = await user._signTypedData(domain, types, value)
const signature = Buffer.concat([
ethUtils.toBuffer(signatureTypedData),
ethUtils.toBuffer(signatureType),
])
const eip712sig = '0x' + signature.toString('hex')
return eip712sig
}

export const buildSignedOrder = async (
signer: Wallet,
order,
order: any,
userAddr: string,
chainId: number,
rfqAddr: string,
Expand All @@ -111,16 +164,42 @@ export const buildSignedOrder = async (
order.salt = generateSaltWithFeeFactor(feeFactor, salt)

const rfqOrer = toRFQOrder(order)

const orderHash = getOrderHash(rfqOrer)
console.log(`orderHash: ${orderHash}`)
const orderSignDigest = getOrderSignDigest(rfqOrer, chainId, rfqAddr)
console.log(`orderSignDigest: ${orderSignDigest}`)
let makerWalletSignature
if (!signingUrl) {
makerWalletSignature =
signer.address.toLowerCase() == order.makerAddress.toLowerCase()
? await signByEOA(orderSignDigest, signer)
: await signByMMPSigner(orderSignDigest, userAddr, feeFactor, signer, walletType)
if (signer.address.toLowerCase() == order.makerAddress.toLowerCase()) {
makerWalletSignature = await signRFQTx(
chainId,
rfqAddr,
rfqOrer,
signer,
rfqOrer.makerAddr,
SignatureType.EIP712
)
} else if (walletType === WalletType.ERC1271_EIP712) {
// standard ERC1271 + ERC712 signature
makerWalletSignature = await signRFQTx(
chainId,
rfqAddr,
rfqOrer,
signer,
rfqOrer.makerAddr,
SignatureType.WalletBytes32
)
} else {
// non-standard wallet contract signature checks
makerWalletSignature = await signByMMPSigner(
orderSignDigest,
userAddr,
feeFactor,
signer,
walletType
)
}
} else {
makerWalletSignature = await forwardUnsignedOrder(signingUrl, {
rfqOrer: rfqOrer,
Expand Down
3 changes: 2 additions & 1 deletion src/signer/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export enum SignatureType {
export enum WalletType {
MMP_VERSION_4 = 1, // https://gist.github.com/NIC619/a3db1a743175bf592f2db983f17680dd#file-mmpv4-sol-L1236
MMP_VERSION_5 = 2, // https://github.com/consenlabs/tokenlon-contracts/blob/e2edf7581b69bc8a40e61ff7fc1cd29674ae4887/contracts/MarketMakerProxy.sol#L19
ERC1271 = 3, // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.6.0/contracts/utils/cryptography/SignatureChecker.sol#L36
ERC1271_EIP712_EIP191 = 3, // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.6.0/contracts/utils/cryptography/SignatureChecker.sol#L36
EOA = 4, // less security for market makers
ERC1271_EIP712 = 5,
}
50 changes: 43 additions & 7 deletions test/new_order.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,7 @@ describe('NewOrder', function () {
mockMarkerMakerConfigUpdater.cacheResult = cacheResult
updaterStack['markerMakerConfigUpdater'] = mockMarkerMakerConfigUpdater
const signedOrderResp = await newOrder({
walletType: WalletType.ERC1271,
walletType: WalletType.ERC1271_EIP712_EIP191,
signer: mmpSigner,
chainID: 1,
quoter: {
Expand Down Expand Up @@ -670,6 +670,7 @@ describe('NewOrder', function () {
expect(signedOrderResp).is.not.null
// verify data object
const order = signedOrderResp.order
console.log(order)
expect(order).is.not.null
expect(order.protocol).eq(Protocol.RFQV1)
expect(order.quoteId).eq('1--echo-testing-8888')
Expand All @@ -695,14 +696,49 @@ describe('NewOrder', function () {
expect(order.takerFee).eq('0')
// verify signature type
const sigBytes = utils.arrayify(signedOrderResp.order.makerWalletSignature)
expect(sigBytes.length).eq(98)
expect(sigBytes[97]).eq(SignatureType.EthSign)
expect(sigBytes.length).eq(66)
expect(sigBytes[65]).eq(SignatureType.EIP712)
// verify signature
const rfqAddr = updaterStack['markerMakerConfigUpdater'].cacheResult.addressBookV5.RFQ
const orderSignDigest = getOrderSignDigest(toRFQOrder(signedOrderResp.order), 1, rfqAddr)
const recovered = utils.verifyMessage(
utils.arrayify(orderSignDigest),
utils.hexlify(sigBytes.slice(0, 65))
const signedOrder = toRFQOrder(signedOrderResp.order)
const domain = {
name: 'Tokenlon',
version: 'v5',
chainId: chainId,
verifyingContract: rfqAddr,
}
const types = {
fillWithPermit: [
{ name: 'makerAddr', type: 'address' },
{ name: 'takerAssetAddr', type: 'address' },
{ name: 'makerAssetAddr', type: 'address' },
{ name: 'takerAssetAmount', type: 'uint256' },
{ name: 'makerAssetAmount', type: 'uint256' },
{ name: 'takerAddr', type: 'address' },
{ name: 'receiverAddr', type: 'address' },
{ name: 'salt', type: 'uint256' },
{ name: 'deadline', type: 'uint256' },
{ name: 'feeFactor', type: 'uint256' },
],
}
// The data to sign
const value = {
makerAddr: signedOrder.makerAddr,
takerAssetAddr: signedOrder.takerAssetAddr,
makerAssetAddr: signedOrder.makerAssetAddr,
takerAssetAmount: signedOrder.takerAssetAmount,
makerAssetAmount: signedOrder.makerAssetAmount,
takerAddr: signedOrder.takerAddr,
receiverAddr: signedOrder.makerAddr,
salt: signedOrder.salt,
deadline: signedOrder.deadline,
feeFactor: signedOrder.feeFactor,
}
const recovered = ethers.utils.verifyTypedData(
domain,
types,
value,
signedOrderResp.order.makerWalletSignature.slice(0, -2)
)
expect(recovered.toLowerCase()).eq(signer.address.toLowerCase())
// verify random values
Expand Down

0 comments on commit b1b30fd

Please sign in to comment.