@gateway.fm/gtw-dvf-client-js
Version:
DVF client js lib with gateway.fm rpc endpoints
114 lines (100 loc) • 3.8 kB
JavaScript
const FP = require('lodash/fp')
const BN = require('bignumber.js')
const Eth = require('@ledgerhq/hw-app-eth').default
const DVFError = require('../dvf/DVFError')
const createSignedOrder = require('../stark/ledger/createSignedOrder')
const selectTransport = require('./selectTransport')
const swJS = require('starkware_crypto')
const getTokenAddressFromTokenInfoOrThrow = require('../dvf/token/getTokenAddressFromTokenInfoOrThrow')
const {
starkTransferTxToMessageHash
} = require('dvf-utils')
const transferTransactionTypes = [
'ConditionalTransferRequest',
'TransferRequest'
]
const getMessage = sw => tx => {
if (!(transferTransactionTypes.includes(tx.type))) {
throw new DVFError(`Unsupported stark transaction type: ${tx.type}`, { tx })
}
return starkTransferTxToMessageHash(sw)(tx)
}
const getTxSignature = async (dvf, tx, path) => {
if (tx.type != null) {
if (!(transferTransactionTypes.includes(tx.type))) {
throw new DVFError(`Unsupported stark transaction type: ${tx.type}`, { tx })
}
const Transport = selectTransport(dvf.isBrowser)
const transport = await Transport.create()
const eth = new Eth(transport)
const { address } = await eth.getAddress(path)
const starkPath = dvf.stark.ledger.getPath(address)
const tokenInfo = dvf.token.getTokenInfoByTokenId(tx.token)
const tokenAddress = getTokenAddressFromTokenInfoOrThrow(tokenInfo, 'ETHEREUM')
const transferQuantization = new BN(tokenInfo.quantization)
const transferAmount = new BN(tx.amount)
let receiverPublicKey = tx.receiverPublicKey
// Will happen if it's an ETH address instead of public key
// ETH addresses a used in the context of StarkEx v4 withdrawals
// Ledger seems to only sign correctly if the ETH address
// is padded as if it was a stark public key
if (receiverPublicKey && receiverPublicKey.length < 66) {
const receiverPublicKeyWithoutPrefix = receiverPublicKey
.slice(2)
.padStart(64, '0')
receiverPublicKey = '0x' + receiverPublicKeyWithoutPrefix
}
try {
// Load token information for Ledger device
const tokenData = await dvf.token.provideContractData(eth, tokenAddress, transferQuantization)
if (tokenData && tokenData.unsafeSign) {
const message = getMessage(swJS)(tx)
const paddedMessage = `0x${message.padEnd(64, '0').substr(-64)}`
return eth.starkUnsafeSign(
starkPath,
paddedMessage
)
}
return eth.starkSignTransfer_v2(
starkPath,
tokenAddress,
tokenAddress ? 'erc20' : 'eth',
transferQuantization,
null,
receiverPublicKey,
tx.senderVaultId,
tx.receiverVaultId,
transferAmount,
tx.nonce,
tx.expirationTimestamp,
tx.type === 'ConditionalTransferRequest' ? tx.factRegistryAddress : null,
tx.type === 'ConditionalTransferRequest' ? tx.fact : null
)
} finally {
await transport.close()
}
} else {
const { starkSignature } = await createSignedOrder(
dvf, path, tx, { returnStarkPublicKey: false }
)
return starkSignature
}
}
module.exports = (dvf) => {
if (!dvf.config.wallet.meta.path) throw new DVFError('LEDGER_PATH_IS_REQUIRED')
let starkPublicKey
const getPublicKey = async () => {
if (starkPublicKey) { return starkPublicKey }
starkPublicKey = await dvf.stark.ledger.getPublicKey(dvf.config.wallet.meta.path)
return starkPublicKey
}
const sign = async tx => {
const starkSignature = await getTxSignature(dvf, tx, dvf.config.wallet.meta.path)
return FP.mapValues(
x => '0x' + x,
starkSignature
)
}
const getWalletType = () => 'LEDGER'
return { sign, getPublicKey, getWalletType }
}