From 281c57f3d5ffbdb2772d54f0249d5ac33c9ff4b5 Mon Sep 17 00:00:00 2001 From: Arman Yaraee Date: Tue, 25 Jul 2023 17:10:02 +0100 Subject: [PATCH] [PI-139]: Generate stark signature --- examples/33.generateL1RegistrationPayload.js | 73 ++++++++++++++++++++ src/lib/dvf/bindApi.js | 5 ++ src/lib/dvf/types.d.ts | 5 ++ src/lib/stark/createRegistrationMessage.js | 45 ++++++++++++ src/lib/stark/signRegistration.js | 39 +++++++++++ 5 files changed, 167 insertions(+) create mode 100755 examples/33.generateL1RegistrationPayload.js create mode 100644 src/lib/dvf/types.d.ts create mode 100644 src/lib/stark/createRegistrationMessage.js create mode 100644 src/lib/stark/signRegistration.js diff --git a/examples/33.generateL1RegistrationPayload.js b/examples/33.generateL1RegistrationPayload.js new file mode 100755 index 00000000..ef528ec9 --- /dev/null +++ b/examples/33.generateL1RegistrationPayload.js @@ -0,0 +1,73 @@ +#!/usr/bin/env -S yarn node +/* eslint-disable no-unused-vars */ + +/* +DO NOT EDIT THIS FILE BY HAND! +Examples are generated using helpers/buildExamples.js script. +Check README.md for more details. +*/ + +const sw = require('@rhino.fi/starkware-crypto') +const getWeb3 = require('./helpers/getWeb3') +const { utils } = require('web3') + +const RhinofiClientFactory = require('../src') +const envVars = require('./helpers/loadFromEnvOrConfig')( + process.env.CONFIG_FILE_NAME +) +const logExampleResult = require('./helpers/logExampleResult')(__filename) + +const ethPrivKey = envVars.ETH_PRIVATE_KEY +// NOTE: you can also generate a new key using:` +// const starkPrivKey = rhinofi.stark.createPrivateKey() +const starkPrivKey = envVars.STARK_PRIVATE_KEY +const rpcUrl = envVars.RPC_URL + +const { web3, provider } = getWeb3(ethPrivKey, rpcUrl) + +const rhinofiConfig = { + api: envVars.API_URL, + dataApi: envVars.DATA_API_URL, + useAuthHeader: true, + wallet: { + type: 'tradingKey', + meta: { + starkPrivateKey: starkPrivKey + } + } + // Add more variables to override default values +} + +;(async () => { + const rhinofi = await RhinofiClientFactory(web3, rhinofiConfig) + const { starkKeyHex, ethAddress } = await rhinofi.getUserConfig() + + console.log('Addresses', ethAddress, starkKeyHex) + + const final = await rhinofi.stark.signRegistration( + starkPrivKey, + ethAddress + ) + + const starkExContract = new web3.eth.Contract( + rhinofi.contract.abi.getStarkEx(), + rhinofi.config.DVF.starkExContractAddress, + ) + + const callData = starkExContract.methods.registerEthAddress( + ethAddress, + starkKeyHex, + final + ).encodeABI() + + logExampleResult({ + ethAddress, + starkKeyHex, + sig: final, + callData + }) +})() +.catch(error => { + console.error(error) + process.exit(1) +}) diff --git a/src/lib/dvf/bindApi.js b/src/lib/dvf/bindApi.js index e50385b7..4c8f3e82 100644 --- a/src/lib/dvf/bindApi.js +++ b/src/lib/dvf/bindApi.js @@ -12,6 +12,9 @@ module.exports = () => { // returns a function that will call api functions prepending dvf // as first argument + /** + * @type {(fn: T) => (...args: ParametersExceptFirst) => ReturnType} + */ const compose = (funk, ...args) => { return _partial(funk, dvf, ...args) } @@ -30,6 +33,8 @@ module.exports = () => { } dvf.stark = { + signRegistration: compose(require('../stark/signRegistration')), + createRegistrationMessage: compose(require('../stark/createRegistrationMessage')), createOrder: compose(require('../stark/createOrder')), createMarketOrder: compose(require('../stark/createMarketOrder')), createOrderMessage: compose(require('../stark/createOrderMessage')), diff --git a/src/lib/dvf/types.d.ts b/src/lib/dvf/types.d.ts new file mode 100644 index 00000000..242f7419 --- /dev/null +++ b/src/lib/dvf/types.d.ts @@ -0,0 +1,5 @@ +/** + * Simple typing file for manually adding types + */ + +type ParametersExceptFirst = F extends (head: any, ...tail: infer R) => any ? R : never \ No newline at end of file diff --git a/src/lib/stark/createRegistrationMessage.js b/src/lib/stark/createRegistrationMessage.js new file mode 100644 index 00000000..59ab8a19 --- /dev/null +++ b/src/lib/stark/createRegistrationMessage.js @@ -0,0 +1,45 @@ +const DVFError = require('../dvf/DVFError') +const sw = require('@rhino.fi/starkware-crypto') +const { utils } = require('web3') + +// Constant from Starkware contract +// https://github.com/starkware-libs/starkex-contracts/blob/210bd5f6bcb6977211677821fe925140859a0f6e/scalable-dex/contracts/src/components/ECDSA.sol#L13-L14 +const EC_ORDER = utils.BN('3618502788666131213697322783095070105526743751716087489154079457884512865583') + +/** + * @type {(dvf: ReturnType, + * starkHex: string, + * ethAddress: string) => utils.BN} + */ +module.exports = (dvf, starkHex, ethAddress) => { + const starkware = dvf.sw || sw + if (!ethAddress) { + throw new Error('ethAddress is required') + } + + if (!starkHex) { + throw new Error('ethAddress is required') + } + + /* + uint256 msgHash = uint256( + keccak256(abi.encodePacked("UserRegistration:", ethKey, starkKey)) + ) % ECDSA.EC_ORDER; + */ + + try { + const hashedMessage = utils.soliditySha3( + utils.encodePacked( + { value: 'UserRegistration:', type: 'string' }, + { value: ethAddress, type: 'address' }, + { value: starkHex, type: 'uint256' }, + ) + ) + + const message = utils.BN(hashedMessage).mod(EC_ORDER).toString(16) + + return message + } catch (error) { + throw new DVFError('ERR_CREATING_STARK_REGISTRATION_MESSAGE', { error }) + } +} diff --git a/src/lib/stark/signRegistration.js b/src/lib/stark/signRegistration.js new file mode 100644 index 00000000..e145c653 --- /dev/null +++ b/src/lib/stark/signRegistration.js @@ -0,0 +1,39 @@ +const DVFError = require('../dvf/DVFError') +const sw = require('@rhino.fi/starkware-crypto') + +const pad = (rec) => rec.padStart(64, 0) + +/** + * @type {(dvf: ReturnType, + * tradingKey: string, + * ethAddress: string) => string} + */ +module.exports = async (dvf, tradingKey, ethAddress) => { + const starkware = dvf.sw || sw + if (!tradingKey) { + throw new Error('tradingKey is required') + } + + /* + uint256 msgHash = uint256( + keccak256(abi.encodePacked("UserRegistration:", ethKey, starkKey)) + ) % ECDSA.EC_ORDER; + */ + + const { dvfStarkProvider } = dvf + const publicKey = await dvfStarkProvider.getPublicKey() + + const message = dvf.stark.createRegistrationMessage(`0x${pad(publicKey.x)}`, ethAddress) + + try { + const { starkKeyPair } = dvf.stark.createKeyPair(tradingKey) + + const { r, s } = starkware.sign(starkKeyPair, message) + + const final = `0x${pad(r.toString(16))}${pad(s.toString(16))}${pad(publicKey.y)}` + + return final + } catch (error) { + throw new DVFError('ERR_CREATING_STARK_REGISTRATION_SIGNATURE', { error }) + } +}