@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 +7 -4
- package/src/tx/async-account.js +38 -0
- package/src/tx/sign-unsigned-tx.js +44 -25
- package/src/vendor/transaction.js +18 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/solana-lib",
|
|
3
|
-
"version": "2.
|
|
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/
|
|
37
|
-
"@
|
|
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": "
|
|
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
|
-
|
|
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,
|
|
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)
|