@exodus/solana-lib 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/solana-lib",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "description": "Exodus internal Solana low-level library",
5
5
  "main": "src/index.js",
6
6
  "files": [
@@ -33,8 +33,11 @@
33
33
  "tweetnacl": "^1.0.3"
34
34
  },
35
35
  "devDependencies": {
36
- "@exodus/solana-meta": "^1.0.7",
37
- "@solana/web3.js": "^1.90.0"
36
+ "@exodus/key-identifier": "1.0.0",
37
+ "@exodus/keychain": "^6.2.0",
38
+ "@exodus/solana-meta": "^1.1.0",
39
+ "@solana/web3.js": "^1.90.0",
40
+ "bip39": "^2.6.0"
38
41
  },
39
- "gitHead": "84f77ed9720dcdabb8057ed9170f45fcb4f9eff9"
42
+ "gitHead": "85fd61e1f2f77aff4c32b5684eb5f4845f06794a"
40
43
  }
@@ -0,0 +1,38 @@
1
+ import { PublicKey } from '../vendor/publickey'
2
+
3
+ export class AsyncSignerAccount {
4
+ #publicKey
5
+ #asyncSigner
6
+
7
+ /**
8
+ * Create a new AsyncSignerAccount object
9
+ *
10
+ * @param asyncSigner A signer object: `{ sign, getPublicKey }`
11
+ */
12
+ constructor(asyncSigner) {
13
+ if (!asyncSigner) throw new Error('please provide a signer object to async signer')
14
+
15
+ this.#asyncSigner = asyncSigner
16
+ this.updatePublicKey().catch((err) =>
17
+ console.error('error getting public key from signer', err)
18
+ )
19
+ }
20
+
21
+ get publicKey() {
22
+ if (!this.#publicKey) throw new Error('public key not yet available in async signer')
23
+ return this.#publicKey
24
+ }
25
+
26
+ get secretKey() {
27
+ throw new Error('secret key not available from async signer')
28
+ }
29
+
30
+ sign = async (signData) => this.#asyncSigner.sign({ data: signData })
31
+
32
+ updatePublicKey = async () => {
33
+ if (!this.#publicKey) {
34
+ const publicKey = await this.#asyncSigner.getPublicKey()
35
+ this.#publicKey = new PublicKey(publicKey)
36
+ }
37
+ }
38
+ }
@@ -3,44 +3,63 @@ import assert from 'minimalistic-assert'
3
3
  import { prepareForSigning } from './prepare-for-signing'
4
4
  import { getKeyPairFromPrivateKey } from '../keypair'
5
5
  import { Account } from '../vendor'
6
+ import { AsyncSignerAccount } from './async-account'
6
7
  import { extractTransaction, isVersionedTransaction } from './common'
7
8
 
9
+ /**
10
+ *
11
+ * @param {*} unsignedTx
12
+ * @param {Object} asyncSigner object: `{ sign: async (buffer) => Promise<>, getPublicKey: async () => Promise<any>}`
13
+ * @returns
14
+ */
15
+ export async function signUnsignedTxWithSigner(unsignedTx, signer) {
16
+ assert(signer, 'Please provide a signer')
17
+
18
+ const tx = prepareForSigning(unsignedTx)
19
+
20
+ const account = new AsyncSignerAccount(signer)
21
+ await account.updatePublicKey()
22
+ await _signTx({ tx, account })
23
+
24
+ return extractTransaction({ tx })
25
+ }
26
+
8
27
  export function signUnsignedTx(unsignedTx, privateKey) {
9
28
  assert(privateKey, 'Please provide a secretKey')
10
29
 
11
30
  const tx = prepareForSigning(unsignedTx)
12
31
 
13
- _signTx({ tx, privateKey })
32
+ const { secretKey } = getKeyPairFromPrivateKey(privateKey)
33
+ const account = new Account(secretKey)
34
+ _signTx({ tx, account })
14
35
 
15
36
  return extractTransaction({ tx })
16
37
  }
17
38
 
18
39
  // Signs plain tx.
19
- const _signTx = ({ tx, privateKey }) => {
20
- const { secretKey } = getKeyPairFromPrivateKey(privateKey)
21
- const account = new Account(secretKey)
40
+ const _signTx = ({ tx, account }) => {
22
41
  if (isVersionedTransaction(tx)) {
23
42
  // VersionedTransaction
24
- tx.sign([account])
25
- } else {
26
- // Legacy Transactions
27
-
28
- // Some transactions that we construct internally are technically not complete.
29
- // They don't contain the empty signature slot for the public key.
30
- const foundEmptySignatureSlot = tx.signatures.find(({ publicKey }) =>
31
- publicKey.equals(account.publicKey)
32
- )
33
- if (!foundEmptySignatureSlot) {
34
- // We could use `setSigners` but maybe this is more robust?
35
- tx.signatures.push({
36
- publicKey: account.publicKey,
37
- signature: null,
38
- })
39
- }
40
-
41
- // We need to use `partialSign()` here because legacy `sign()` will
42
- // delete all existing signatures which isn't great if we're
43
- // signing a transaction that already has signatures.
44
- tx.partialSign(account)
43
+ return tx.sign([account])
45
44
  }
45
+
46
+ // Legacy Transactions
47
+
48
+ // Some transactions that we construct internally are technically not complete.
49
+ // They don't contain the empty signature slot for the public key.
50
+ const foundEmptySignatureSlot = tx.signatures.find(({ publicKey }) =>
51
+ publicKey.equals(account.publicKey)
52
+ )
53
+ if (!foundEmptySignatureSlot) {
54
+ // We could use `setSigners` but maybe this is more robust?
55
+ tx.signatures.push({
56
+ publicKey: account.publicKey,
57
+ signature: null,
58
+ })
59
+ }
60
+
61
+ // We need to use `partialSign()` here because legacy `sign()` will
62
+ // delete all existing signatures which isn't great if we're
63
+ // signing a transaction that already has signatures.
64
+ return tx.partialSign(account)
46
65
  }
@@ -21,6 +21,8 @@ import * as shortvec from './utils/shortvec-encoding'
21
21
  const DEFAULT_SIGNATURE = Buffer.alloc(64).fill(0)
22
22
  const SIGNATURE_LENGTH = 64
23
23
 
24
+ const isAsyncAccount = (signer) => signer.sign && signer.publicKey && signer.updatePublicKey
25
+
24
26
  /**
25
27
  * Account metadata used to define instructions
26
28
  *
@@ -412,7 +414,7 @@ export class Transaction {
412
414
  publicKey: signer.publicKey,
413
415
  }))
414
416
 
415
- this.partialSign(...signers)
417
+ return this.partialSign(...signers)
416
418
  }
417
419
 
418
420
  /**
@@ -434,6 +436,21 @@ export class Transaction {
434
436
  })
435
437
 
436
438
  const signData = message.serialize()
439
+
440
+ const isAsyncSign = signers.some(isAsyncAccount)
441
+ if (isAsyncSign) {
442
+ return Promise.all(
443
+ signers.map(async (signer) => ({
444
+ signature: isAsyncAccount(signer)
445
+ ? await signer.sign(signData) // Promise
446
+ : nacl.sign.detached(signData, signer.secretKey),
447
+ publicKey: signer.publicKey,
448
+ }))
449
+ ).then((signatures) =>
450
+ signatures.forEach(({ publicKey, signature }) => this.addSignature(publicKey, signature))
451
+ )
452
+ }
453
+
437
454
  signers.forEach((signer) => {
438
455
  const signature = nacl.sign.detached(signData, signer.secretKey)
439
456
  this.addSignature(signer.publicKey, signature)