@dfns/lib-ethersjs5
Advanced tools
Comparing version 0.1.2 to 0.1.3-rc.1
@@ -7,12 +7,18 @@ import { DfnsApiClient } from '@dfns/sdk'; | ||
dfnsClient: DfnsApiClient; | ||
/** @deprecated transaction signing is now synchronous. polling is deprecated. */ | ||
maxRetries?: number; | ||
/** @deprecated transaction signing is now synchronous. polling is deprecated. */ | ||
retryInterval?: number; | ||
}; | ||
export declare class DfnsWallet extends Signer implements TypedDataSigner { | ||
private options; | ||
private address?; | ||
private options; | ||
private metadata?; | ||
/** @deprecated use DfnsWallet.init(options) instead */ | ||
constructor(options: DfnsWalletOptions, provider?: Provider | null); | ||
static init(options: DfnsWalletOptions): Promise<DfnsWallet>; | ||
connect(provider: Provider | null): Signer; | ||
private getCachedMetadata; | ||
getAddress(): Promise<string>; | ||
waitForSignature(signatureId: string): Promise<string>; | ||
private signHash; | ||
signTransaction(transaction: TransactionRequest): Promise<string>; | ||
@@ -19,0 +25,0 @@ signMessage(message: string | Uint8Array): Promise<string>; |
193
index.js
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.DfnsWallet = void 0; | ||
const Wallets_1 = require("@dfns/sdk/codegen/datamodel/Wallets"); | ||
const sdk_1 = require("@dfns/sdk"); | ||
const address_1 = require("@ethersproject/address"); | ||
const abstract_signer_1 = require("@ethersproject/abstract-signer"); | ||
const bignumber_1 = require("@ethersproject/bignumber"); | ||
const bytes_1 = require("@ethersproject/bytes"); | ||
@@ -11,55 +12,88 @@ const hash_1 = require("@ethersproject/hash"); | ||
const properties_1 = require("@ethersproject/properties"); | ||
const strings_1 = require("@ethersproject/strings"); | ||
const transactions_1 = require("@ethersproject/transactions"); | ||
const sleep = (interval = 0) => new Promise((resolve) => setTimeout(resolve, interval)); | ||
const assertSigned = (res) => { | ||
if (res.status === 'Failed') { | ||
throw new sdk_1.DfnsError(-1, 'signing failed', res); | ||
} | ||
else if (res.status !== 'Signed') { | ||
throw new sdk_1.DfnsError(-1, 'cannot complete signing synchronously because this wallet action requires policy approval', res); | ||
} | ||
}; | ||
const combineSignature = (res) => { | ||
if (!res.signature) { | ||
throw new sdk_1.DfnsError(-1, 'signature missing', res); | ||
} | ||
const { r, s, recid } = res.signature; | ||
return (0, bytes_1.joinSignature)({ | ||
r, | ||
s, | ||
recoveryParam: recid, | ||
}); | ||
}; | ||
const boundToEvmNetwork = (wallet) => { | ||
// if the address is evm format, it's a wallet bound to evm network. prefer to | ||
// sign the full transaction instead of the hash of the transaction | ||
return wallet.address ? !!wallet.address.match(/^0x[0-9a-fA-F]{40}$/) : false; | ||
}; | ||
const fetchWalletMetadata = async (options) => { | ||
const { walletId, dfnsClient } = options; | ||
const wallet = await dfnsClient.wallets.getWallet({ walletId }); | ||
if (wallet.status !== 'Active') { | ||
throw new sdk_1.DfnsError(-1, 'wallet not active', { walletId, status: wallet.status }); | ||
} | ||
const { scheme, curve } = wallet.signingKey; | ||
if (scheme !== 'ECDSA') { | ||
throw new sdk_1.DfnsError(-1, 'key scheme is not ECDSA', { walletId, scheme }); | ||
} | ||
if (curve !== 'secp256k1') { | ||
throw new sdk_1.DfnsError(-1, 'key curve is not secp256k1', { walletId, curve }); | ||
} | ||
return { | ||
boundToEvmNetwork: boundToEvmNetwork(wallet), | ||
...wallet, | ||
}; | ||
}; | ||
class DfnsWallet extends abstract_signer_1.Signer { | ||
/** @deprecated use DfnsWallet.init(options) instead */ | ||
constructor(options, provider) { | ||
super(); | ||
this.options = { | ||
...options, | ||
maxRetries: options.maxRetries ?? 3, | ||
retryInterval: options.retryInterval ?? 1000, | ||
}; | ||
this.options = options; | ||
(0, properties_1.defineReadOnly)(this, 'provider', provider || undefined); | ||
} | ||
static async init(options) { | ||
const metadata = await fetchWalletMetadata(options); | ||
const wallet = new DfnsWallet(options); | ||
wallet.metadata = metadata; | ||
return wallet; | ||
} | ||
connect(provider) { | ||
return new DfnsWallet(this.options, provider); | ||
const copy = new DfnsWallet(this.options, provider); | ||
copy.address = this.address; | ||
copy.metadata = this.metadata; | ||
return copy; | ||
} | ||
async getCachedMetadata() { | ||
if (!this.metadata) { | ||
this.metadata = await fetchWalletMetadata(this.options); | ||
} | ||
return this.metadata; | ||
} | ||
async getAddress() { | ||
if (!this.address) { | ||
const { walletId, dfnsClient } = this.options; | ||
const res = await dfnsClient.wallets.getWallet({ walletId }); | ||
if (!res.signingKey || res.signingKey.scheme !== Wallets_1.KeyScheme.ECDSA || res.signingKey.curve !== Wallets_1.KeyCurve.secp256k1) { | ||
throw new Error(`wallet ${walletId} has incompatible scheme (${res.signingKey?.scheme}) or curve (${res.signingKey?.curve})`); | ||
} | ||
if (res.address) { | ||
this.address = (0, address_1.getAddress)(res.address); | ||
} | ||
else { | ||
this.address = (0, transactions_1.computeAddress)('0x' + res.signingKey.publicKey); | ||
} | ||
const metadata = await this.getCachedMetadata(); | ||
this.address = metadata.boundToEvmNetwork | ||
? (0, address_1.getAddress)(metadata.address) | ||
: (0, transactions_1.computeAddress)('0x' + metadata.signingKey.publicKey); | ||
} | ||
return this.address; | ||
} | ||
async waitForSignature(signatureId) { | ||
const { walletId, dfnsClient, retryInterval } = this.options; | ||
let maxRetries = this.options.maxRetries; | ||
while (maxRetries > 0) { | ||
await sleep(retryInterval); | ||
const res = await dfnsClient.wallets.getSignature({ walletId, signatureId }); | ||
if (res.status === Wallets_1.SignatureStatus.Signed) { | ||
if (!res.signature) | ||
break; | ||
return (0, bytes_1.joinSignature)({ | ||
r: res.signature.r, | ||
s: res.signature.s, | ||
recoveryParam: res.signature.recid, | ||
}); | ||
} | ||
else if (res.status === Wallets_1.SignatureStatus.Failed) { | ||
break; | ||
} | ||
maxRetries -= 1; | ||
} | ||
const waitedSeconds = Math.floor((this.options.maxRetries * retryInterval) / 1000); | ||
throw new Error(`Signature request ${signatureId} took more than ${waitedSeconds}s to complete, stopping polling. Please update options "maxRetries" or "retryIntervals" to wait longer.`); | ||
async signHash(hash) { | ||
const metadata = await this.getCachedMetadata(); | ||
const res = await this.options.dfnsClient.wallets.generateSignature({ | ||
walletId: metadata.id, | ||
body: { kind: 'Hash', hash }, | ||
}); | ||
assertSigned(res); | ||
return combineSignature(res); | ||
} | ||
@@ -74,18 +108,37 @@ async signTransaction(transaction) { | ||
} | ||
const { walletId, dfnsClient } = this.options; | ||
const res = await dfnsClient.wallets.generateSignature({ | ||
walletId, | ||
body: { kind: Wallets_1.SignatureKind.Hash, hash: (0, keccak256_1.keccak256)((0, transactions_1.serialize)(tx)) }, | ||
}); | ||
const signature = await this.waitForSignature(res.id); | ||
return (0, transactions_1.serialize)(tx, signature); | ||
const unsigned = (0, transactions_1.serialize)(tx); | ||
const metadata = await this.getCachedMetadata(); | ||
if (metadata.boundToEvmNetwork) { | ||
const res = await this.options.dfnsClient.wallets.generateSignature({ | ||
walletId: metadata.id, | ||
body: { kind: 'Transaction', transaction: unsigned }, | ||
}); | ||
assertSigned(res); | ||
if (!res.signedData) { | ||
throw new sdk_1.DfnsError(-1, 'signedData missing', res); | ||
} | ||
return res.signedData; | ||
} | ||
else { | ||
const signature = await this.signHash((0, keccak256_1.keccak256)(unsigned)); | ||
return (0, transactions_1.serialize)(tx, signature); | ||
} | ||
}); | ||
} | ||
async signMessage(message) { | ||
const { walletId, dfnsClient } = this.options; | ||
const res = await dfnsClient.wallets.generateSignature({ | ||
walletId, | ||
body: { kind: Wallets_1.SignatureKind.Hash, hash: (0, hash_1.hashMessage)(message) }, | ||
}); | ||
return this.waitForSignature(res.id); | ||
const metadata = await this.getCachedMetadata(); | ||
if (metadata.boundToEvmNetwork) { | ||
if (typeof message === 'string') { | ||
message = (0, strings_1.toUtf8Bytes)(message); | ||
} | ||
const res = await this.options.dfnsClient.wallets.generateSignature({ | ||
walletId: metadata.id, | ||
body: { kind: 'Message', message: (0, bytes_1.hexlify)(message) }, | ||
}); | ||
assertSigned(res); | ||
return combineSignature(res); | ||
} | ||
else { | ||
return this.signHash((0, hash_1.hashMessage)(message)); | ||
} | ||
} | ||
@@ -103,13 +156,27 @@ async _signTypedData(domain, types, value) { | ||
}); | ||
const { walletId, dfnsClient } = this.options; | ||
const res = await dfnsClient.wallets.generateSignature({ | ||
walletId, | ||
body: { | ||
kind: Wallets_1.SignatureKind.Hash, | ||
hash: hash_1._TypedDataEncoder.hash(populated.domain, types, populated.value), | ||
}, | ||
}); | ||
return this.waitForSignature(res.id); | ||
const metadata = await this.getCachedMetadata(); | ||
if (metadata.boundToEvmNetwork) { | ||
const res = await this.options.dfnsClient.wallets.generateSignature({ | ||
walletId: metadata.id, | ||
body: { | ||
kind: 'Eip712', | ||
types, | ||
domain: { | ||
name: populated.domain.name ?? undefined, | ||
version: populated.domain.version ?? undefined, | ||
chainId: populated.domain.chainId ? bignumber_1.BigNumber.from(populated.domain.chainId).toNumber() : undefined, | ||
verifyingContract: populated.domain.verifyingContract ?? undefined, | ||
salt: populated.domain.salt ? (0, bytes_1.hexlify)(populated.domain.salt) : undefined, | ||
}, | ||
message: populated.value, | ||
}, | ||
}); | ||
assertSigned(res); | ||
return combineSignature(res); | ||
} | ||
else { | ||
return this.signHash(hash_1._TypedDataEncoder.hash(populated.domain, types, populated.value)); | ||
} | ||
} | ||
} | ||
exports.DfnsWallet = DfnsWallet; |
{ | ||
"name": "@dfns/lib-ethersjs5", | ||
"version": "0.1.2", | ||
"version": "0.1.3-rc.1", | ||
"dependencies": { | ||
@@ -8,2 +8,3 @@ "@ethersproject/abstract-provider": "5.7.0", | ||
"@ethersproject/address": "5.7.0", | ||
"@ethersproject/bignumber": "5.7.0", | ||
"@ethersproject/bytes": "5.7.0", | ||
@@ -13,2 +14,3 @@ "@ethersproject/hash": "5.7.0", | ||
"@ethersproject/properties": "5.7.0", | ||
"@ethersproject/strings": "5.7.0", | ||
"@ethersproject/transactions": "5.7.0", | ||
@@ -20,3 +22,3 @@ "buffer": "6.0.3", | ||
"peerDependencies": { | ||
"@dfns/sdk": "0.1.2" | ||
"@dfns/sdk": "0.1.3-rc.1" | ||
}, | ||
@@ -23,0 +25,0 @@ "main": "./index.js", |
9325
205
14
+ Added@ethersproject/strings@5.7.0
+ Added@dfns/sdk@0.1.3-rc.1(transitive)
- Removed@dfns/sdk@0.1.2(transitive)