@exodus/solana-lib 1.2.8 → 1.2.9-build2
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/lib/constants.js +19 -0
- package/lib/encode.js +67 -0
- package/lib/fee-data/index.js +15 -0
- package/lib/fee-data/solana.js +14 -0
- package/lib/helpers/spl-token.js +122 -0
- package/lib/helpers/tokenTransfer.js +78 -0
- package/lib/index.js +88 -0
- package/lib/keypair.js +38 -0
- package/lib/tokens.js +21 -0
- package/lib/transaction.js +338 -0
- package/lib/tx/create-and-sign-tx.js +15 -0
- package/{src → lib}/tx/create-unsigned-tx.js +20 -11
- package/lib/tx/index.js +53 -0
- package/lib/tx/parse-unsigned-tx.js +55 -0
- package/lib/tx/sign-unsigned-tx.js +90 -0
- package/lib/vendor/account.js +48 -0
- package/lib/vendor/index.js +113 -0
- package/lib/vendor/instruction.js +48 -0
- package/lib/vendor/message.js +167 -0
- package/lib/vendor/nonce-account.js +56 -0
- package/lib/vendor/publickey.js +211 -0
- package/lib/vendor/stake-program.js +476 -0
- package/lib/vendor/system-program.js +640 -0
- package/lib/vendor/sysvar.js +19 -0
- package/lib/vendor/transaction.js +594 -0
- package/lib/vendor/utils/blockhash.js +1 -0
- package/lib/vendor/utils/fee-calculator.js +25 -0
- package/lib/vendor/utils/layout.js +97 -0
- package/lib/vendor/utils/shortvec-encoding.js +41 -0
- package/lib/vendor/utils/to-buffer.js +18 -0
- package/package.json +4 -5
- package/src/constants.js +0 -12
- package/src/encode.js +0 -57
- package/src/fee-data/index.js +0 -1
- package/src/fee-data/solana.js +0 -9
- package/src/helpers/spl-token.js +0 -108
- package/src/helpers/tokenTransfer.js +0 -72
- package/src/index.js +0 -8
- package/src/keypair.js +0 -32
- package/src/tokens.js +0 -19
- package/src/transaction.js +0 -240
- package/src/tx/create-and-sign-tx.js +0 -8
- package/src/tx/index.js +0 -4
- package/src/tx/parse-unsigned-tx.js +0 -39
- package/src/tx/sign-unsigned-tx.js +0 -78
- package/src/vendor/account.js +0 -38
- package/src/vendor/index.js +0 -9
- package/src/vendor/instruction.js +0 -46
- package/src/vendor/message.js +0 -216
- package/src/vendor/nonce-account.js +0 -46
- package/src/vendor/publickey.js +0 -212
- package/src/vendor/stake-program.js +0 -527
- package/src/vendor/system-program.js +0 -782
- package/src/vendor/sysvar.js +0 -16
- package/src/vendor/transaction.js +0 -594
- package/src/vendor/utils/blockhash.js +0 -6
- package/src/vendor/utils/fee-calculator.js +0 -17
- package/src/vendor/utils/layout.js +0 -80
- package/src/vendor/utils/shortvec-encoding.js +0 -30
- package/src/vendor/utils/to-buffer.js +0 -9
package/src/transaction.js
DELETED
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
import assert from 'assert'
|
|
3
|
-
|
|
4
|
-
import { getKeyPairFromPrivateKey } from './keypair'
|
|
5
|
-
import { findAssociatedTokenAddress, createStakeAddress } from './encode'
|
|
6
|
-
import {
|
|
7
|
-
createAssociatedTokenAccount,
|
|
8
|
-
createTokenTransferInstruction,
|
|
9
|
-
} from './helpers/tokenTransfer'
|
|
10
|
-
import {
|
|
11
|
-
PublicKey,
|
|
12
|
-
Account,
|
|
13
|
-
Transaction,
|
|
14
|
-
SystemProgram,
|
|
15
|
-
StakeProgram,
|
|
16
|
-
Authorized,
|
|
17
|
-
Lockup,
|
|
18
|
-
} from './vendor'
|
|
19
|
-
import { SEED } from './constants'
|
|
20
|
-
import TOKENS from './tokens'
|
|
21
|
-
import bs58 from 'bs58'
|
|
22
|
-
|
|
23
|
-
class Tx {
|
|
24
|
-
constructor({
|
|
25
|
-
from,
|
|
26
|
-
to,
|
|
27
|
-
amount,
|
|
28
|
-
recentBlockhash,
|
|
29
|
-
// fee, // (Fee per Signature: 5000 lamports)
|
|
30
|
-
// Tokens related:
|
|
31
|
-
tokenName,
|
|
32
|
-
destinationAddressType,
|
|
33
|
-
isAssociatedTokenAccountActive, // true when recipient balance !== 0
|
|
34
|
-
fromTokenAddresses, // sender token addresses
|
|
35
|
-
} = {}) {
|
|
36
|
-
assert(from, 'from is required')
|
|
37
|
-
assert(to, 'to is required')
|
|
38
|
-
assert(amount, 'amount is required')
|
|
39
|
-
assert(typeof amount === 'number', 'amount must be a number')
|
|
40
|
-
assert(recentBlockhash, 'recentBlockhash is required')
|
|
41
|
-
if (tokenName) {
|
|
42
|
-
assert(
|
|
43
|
-
typeof destinationAddressType !== 'undefined',
|
|
44
|
-
'destinationAddressType is required when sending tokens'
|
|
45
|
-
)
|
|
46
|
-
assert(
|
|
47
|
-
typeof isAssociatedTokenAccountActive !== 'undefined',
|
|
48
|
-
'isAssociatedTokenAccountActive is required when sending tokens'
|
|
49
|
-
) // needed to create the recipient account
|
|
50
|
-
assert(Array.isArray(fromTokenAddresses), 'fromTokenAddresses Array is required')
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const token = TOKENS.find(({ tokenName: name }) => name === tokenName)
|
|
54
|
-
this.txObj = {
|
|
55
|
-
from,
|
|
56
|
-
to,
|
|
57
|
-
amount,
|
|
58
|
-
recentBlockhash,
|
|
59
|
-
token,
|
|
60
|
-
destinationAddressType,
|
|
61
|
-
isAssociatedTokenAccountActive,
|
|
62
|
-
fromTokenAddresses,
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (token) {
|
|
66
|
-
// TOKEN transfer tx
|
|
67
|
-
this.buildTokenTransaction(this.txObj)
|
|
68
|
-
} else {
|
|
69
|
-
// SOL tx
|
|
70
|
-
this.buildSOLtransaction(this.txObj)
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
buildSOLtransaction({ from, to, amount, recentBlockhash }) {
|
|
75
|
-
const txInstruction = SystemProgram.transfer({
|
|
76
|
-
fromPubkey: new PublicKey(from),
|
|
77
|
-
toPubkey: new PublicKey(to),
|
|
78
|
-
lamports: amount,
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
this.transaction = new Transaction({
|
|
82
|
-
instructions: [txInstruction],
|
|
83
|
-
recentBlockhash,
|
|
84
|
-
})
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
buildTokenTransaction({
|
|
88
|
-
from,
|
|
89
|
-
to,
|
|
90
|
-
amount,
|
|
91
|
-
recentBlockhash,
|
|
92
|
-
token,
|
|
93
|
-
destinationAddressType,
|
|
94
|
-
isAssociatedTokenAccountActive,
|
|
95
|
-
fromTokenAddresses,
|
|
96
|
-
}) {
|
|
97
|
-
this.transaction = new Transaction({
|
|
98
|
-
recentBlockhash,
|
|
99
|
-
})
|
|
100
|
-
const isUnknown = destinationAddressType === null
|
|
101
|
-
const isSOLaddress = destinationAddressType === 'solana'
|
|
102
|
-
// crete account instruction
|
|
103
|
-
if (isUnknown) throw new Error('Destination SOL balance cannot be zero (address not active)') // cannot initialize without knowing the owner
|
|
104
|
-
if (isSOLaddress && !isAssociatedTokenAccountActive)
|
|
105
|
-
this.transaction.add(createAssociatedTokenAccount(from, token.mintAddress, to))
|
|
106
|
-
|
|
107
|
-
let amountLeft = amount
|
|
108
|
-
let amountToSend
|
|
109
|
-
for (let { ticker, tokenAccountAddress, balance } of fromTokenAddresses) {
|
|
110
|
-
// need to add more of this instruction until we reach the desired balance (amount) to send
|
|
111
|
-
assert(ticker === token.tokenSymbol, `Got unexpected ticker ${ticker}`)
|
|
112
|
-
if (amountLeft === 0) break
|
|
113
|
-
|
|
114
|
-
balance = Number(balance)
|
|
115
|
-
if (balance >= amountLeft) {
|
|
116
|
-
amountToSend = amountLeft
|
|
117
|
-
amountLeft = 0
|
|
118
|
-
} else {
|
|
119
|
-
amountToSend = balance // not enough balance
|
|
120
|
-
amountLeft -= amountToSend
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const dest = isSOLaddress ? findAssociatedTokenAddress(to, token.mintAddress) : to
|
|
124
|
-
// add transfer token instruction
|
|
125
|
-
this.transaction.add(
|
|
126
|
-
createTokenTransferInstruction(from, tokenAccountAddress, dest, amountToSend)
|
|
127
|
-
)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
assert(amountLeft === 0, `Not enough balance to send ${amount} ${token.tokenSymbol}`)
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
static createStakeAccountTransaction({ address, amount, seed = SEED, pool, recentBlockhash }) {
|
|
134
|
-
const fromPubkey = new PublicKey(address)
|
|
135
|
-
const stakeAddress = createStakeAddress(address, seed)
|
|
136
|
-
const stakePublicKey = new PublicKey(stakeAddress)
|
|
137
|
-
const poolKey = new PublicKey(pool)
|
|
138
|
-
|
|
139
|
-
const authorized = new Authorized(fromPubkey, fromPubkey) // staker, withdrawer
|
|
140
|
-
const lockup = new Lockup(0, 0, fromPubkey) // no lockup
|
|
141
|
-
|
|
142
|
-
// create account instruction
|
|
143
|
-
const programTx = StakeProgram.createAccountWithSeed({
|
|
144
|
-
fromPubkey,
|
|
145
|
-
stakePubkey: stakePublicKey,
|
|
146
|
-
basePubkey: fromPubkey,
|
|
147
|
-
seed,
|
|
148
|
-
authorized,
|
|
149
|
-
lockup,
|
|
150
|
-
lamports: amount, // number
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
// delegate funds instruction
|
|
154
|
-
const delegateTx = StakeProgram.delegate({
|
|
155
|
-
stakePubkey: stakePublicKey,
|
|
156
|
-
authorizedPubkey: fromPubkey,
|
|
157
|
-
votePubkey: poolKey, // pool vote key
|
|
158
|
-
})
|
|
159
|
-
|
|
160
|
-
const transaction = new Transaction({ recentBlockhash }).add(programTx).add(delegateTx)
|
|
161
|
-
return transaction
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
static undelegate({ address, stakeAddresses, recentBlockhash }) {
|
|
165
|
-
// undelegate all stake addresses
|
|
166
|
-
assert(Array.isArray(stakeAddresses), 'stakeAddresses Array is required')
|
|
167
|
-
|
|
168
|
-
const fromPubkey = new PublicKey(address)
|
|
169
|
-
const transaction = new Transaction({ recentBlockhash })
|
|
170
|
-
|
|
171
|
-
stakeAddresses.forEach((stakeAddress) => {
|
|
172
|
-
const stakePublicKey = new PublicKey(stakeAddress)
|
|
173
|
-
const programTx = StakeProgram.deactivate({
|
|
174
|
-
stakePubkey: stakePublicKey,
|
|
175
|
-
authorizedPubkey: fromPubkey,
|
|
176
|
-
})
|
|
177
|
-
transaction.add(programTx)
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
return transaction
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
static withdraw({ address, stakeAddresses, amount, recentBlockhash }) {
|
|
184
|
-
const fromPubkey = new PublicKey(address)
|
|
185
|
-
const stakeAddress = Array.isArray(stakeAddresses) ? stakeAddresses[0] : stakeAddresses
|
|
186
|
-
const stakePublicKey = new PublicKey(stakeAddress)
|
|
187
|
-
|
|
188
|
-
const transaction = StakeProgram.withdraw({
|
|
189
|
-
stakePubkey: stakePublicKey,
|
|
190
|
-
authorizedPubkey: fromPubkey,
|
|
191
|
-
toPubkey: fromPubkey,
|
|
192
|
-
lamports: amount,
|
|
193
|
-
})
|
|
194
|
-
transaction.recentBlockhash = recentBlockhash
|
|
195
|
-
return transaction
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
static sign(tx, privateKey: Buffer | string) {
|
|
199
|
-
if (!privateKey) throw new Error('Please provide a secretKey')
|
|
200
|
-
const { secretKey } = getKeyPairFromPrivateKey(privateKey)
|
|
201
|
-
const signers = [new Account(secretKey)]
|
|
202
|
-
|
|
203
|
-
let transaction = tx
|
|
204
|
-
if (tx instanceof Tx) transaction = tx.transaction
|
|
205
|
-
|
|
206
|
-
transaction.sign(...signers)
|
|
207
|
-
if (!transaction.signature) {
|
|
208
|
-
throw new Error('!signature') // should never happen
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
serialize() {
|
|
213
|
-
const wireTransaction = this.transaction.serialize()
|
|
214
|
-
return wireTransaction.toString('base64')
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
static serialize(tx) {
|
|
218
|
-
let transaction = tx
|
|
219
|
-
if (tx instanceof Tx) transaction = tx.transaction
|
|
220
|
-
return transaction.serialize().toString('base64')
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
getTxId() {
|
|
224
|
-
if (!this.transaction.signature) {
|
|
225
|
-
throw new Error('Cannot get txId, tx is not signed')
|
|
226
|
-
}
|
|
227
|
-
return bs58.encode(this.transaction.signature)
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
static getTxId(tx) {
|
|
231
|
-
let transaction = tx
|
|
232
|
-
if (tx instanceof Tx) transaction = tx.transaction
|
|
233
|
-
if (!transaction.signature) {
|
|
234
|
-
throw new Error('Cannot get txId, tx is not signed')
|
|
235
|
-
}
|
|
236
|
-
return bs58.encode(transaction.signature)
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
export default Tx
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { createUnsignedTx } from './create-unsigned-tx'
|
|
2
|
-
import { signUnsignedTx } from './sign-unsigned-tx'
|
|
3
|
-
import type { ParsedTransaction, SignedTransaction } from '@exodus/models/lib/types'
|
|
4
|
-
|
|
5
|
-
export function createAndSignTx(input: ParsedTransaction, privateKey: Buffer): SignedTransaction {
|
|
6
|
-
const unsignedTx = createUnsignedTx(input)
|
|
7
|
-
return signUnsignedTx(unsignedTx, privateKey)
|
|
8
|
-
}
|
package/src/tx/index.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import assets from '@exodus/assets'
|
|
2
|
-
import type { UnsignedTransaction, ParsedTransaction } from '@exodus/models/lib/types'
|
|
3
|
-
|
|
4
|
-
export function parseUnsignedTx(unsignedTx: UnsignedTransaction): ParsedTransaction {
|
|
5
|
-
const asset = assets.solana
|
|
6
|
-
const {
|
|
7
|
-
from,
|
|
8
|
-
to,
|
|
9
|
-
recentBlockhash,
|
|
10
|
-
tokenName,
|
|
11
|
-
destinationAddressType,
|
|
12
|
-
isAssociatedTokenAccountActive,
|
|
13
|
-
fromTokenAddresses,
|
|
14
|
-
method,
|
|
15
|
-
stakeAddresses,
|
|
16
|
-
seed,
|
|
17
|
-
pool,
|
|
18
|
-
...txData
|
|
19
|
-
} = unsignedTx.txData
|
|
20
|
-
|
|
21
|
-
const amount = asset.currency.baseUnit(txData.amount)
|
|
22
|
-
return {
|
|
23
|
-
asset,
|
|
24
|
-
from: [from],
|
|
25
|
-
to,
|
|
26
|
-
amount,
|
|
27
|
-
recentBlockhash,
|
|
28
|
-
// token related
|
|
29
|
-
tokenName,
|
|
30
|
-
destinationAddressType,
|
|
31
|
-
isAssociatedTokenAccountActive,
|
|
32
|
-
fromTokenAddresses,
|
|
33
|
-
// staking related
|
|
34
|
-
method,
|
|
35
|
-
stakeAddresses,
|
|
36
|
-
seed,
|
|
37
|
-
pool,
|
|
38
|
-
}
|
|
39
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import assets from '@exodus/assets'
|
|
2
|
-
import type { UnsignedTransaction, SignedTransaction } from '@exodus/models/lib/types'
|
|
3
|
-
import { Transaction } from '../'
|
|
4
|
-
|
|
5
|
-
export function signUnsignedTx(
|
|
6
|
-
unsignedTx: UnsignedTransaction,
|
|
7
|
-
privateKey: Buffer
|
|
8
|
-
): SignedTransaction {
|
|
9
|
-
const {
|
|
10
|
-
from,
|
|
11
|
-
to,
|
|
12
|
-
amount: unitAmount,
|
|
13
|
-
recentBlockhash,
|
|
14
|
-
// tokens related
|
|
15
|
-
tokenName,
|
|
16
|
-
destinationAddressType,
|
|
17
|
-
isAssociatedTokenAccountActive,
|
|
18
|
-
fromTokenAddresses,
|
|
19
|
-
// staking related
|
|
20
|
-
stakeAddresses,
|
|
21
|
-
method,
|
|
22
|
-
seed,
|
|
23
|
-
pool,
|
|
24
|
-
} = unsignedTx.txData
|
|
25
|
-
|
|
26
|
-
const asset = assets.solana
|
|
27
|
-
|
|
28
|
-
const address = from
|
|
29
|
-
const amount = unitAmount ? asset.currency.baseUnit(unitAmount).toNumber() : unitAmount
|
|
30
|
-
|
|
31
|
-
let tx
|
|
32
|
-
switch (method) {
|
|
33
|
-
case 'delegate':
|
|
34
|
-
tx = Transaction.createStakeAccountTransaction({
|
|
35
|
-
address,
|
|
36
|
-
amount,
|
|
37
|
-
recentBlockhash,
|
|
38
|
-
seed,
|
|
39
|
-
pool,
|
|
40
|
-
})
|
|
41
|
-
break
|
|
42
|
-
case 'undelegate':
|
|
43
|
-
tx = Transaction.undelegate({
|
|
44
|
-
address,
|
|
45
|
-
stakeAddresses,
|
|
46
|
-
recentBlockhash,
|
|
47
|
-
})
|
|
48
|
-
break
|
|
49
|
-
case 'withdraw':
|
|
50
|
-
tx = Transaction.withdraw({
|
|
51
|
-
address,
|
|
52
|
-
stakeAddresses,
|
|
53
|
-
amount,
|
|
54
|
-
recentBlockhash,
|
|
55
|
-
})
|
|
56
|
-
break
|
|
57
|
-
default:
|
|
58
|
-
// SOL and Token tx
|
|
59
|
-
tx = new Transaction({
|
|
60
|
-
from,
|
|
61
|
-
to,
|
|
62
|
-
amount,
|
|
63
|
-
recentBlockhash,
|
|
64
|
-
tokenName,
|
|
65
|
-
destinationAddressType,
|
|
66
|
-
isAssociatedTokenAccountActive,
|
|
67
|
-
fromTokenAddresses,
|
|
68
|
-
})
|
|
69
|
-
break
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// sign plain tx
|
|
73
|
-
Transaction.sign(tx, privateKey)
|
|
74
|
-
const rawTx = Transaction.serialize(tx)
|
|
75
|
-
const txId = Transaction.getTxId(tx)
|
|
76
|
-
|
|
77
|
-
return { txId, rawTx }
|
|
78
|
-
}
|
package/src/vendor/account.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
import type { KeyPair } from 'tweetnacl'
|
|
3
|
-
import { PublicKey } from './publickey'
|
|
4
|
-
import { getKeyPairFromPrivateKey } from '../keypair'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* An account key pair (public and secret keys).
|
|
8
|
-
*/
|
|
9
|
-
export class Account {
|
|
10
|
-
_keypair: KeyPair
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Create a new Account object
|
|
14
|
-
*
|
|
15
|
-
* @param secretKey Secret key for the account
|
|
16
|
-
*/
|
|
17
|
-
constructor(secretKey: Buffer | Uint8Array | Array<number>) {
|
|
18
|
-
if (secretKey) {
|
|
19
|
-
this._keypair = getKeyPairFromPrivateKey(Buffer.from(secretKey, 'hex'))
|
|
20
|
-
} else {
|
|
21
|
-
throw new Error('Please provide a secretKey')
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* The public key for this account
|
|
27
|
-
*/
|
|
28
|
-
get publicKey(): PublicKey {
|
|
29
|
-
return new PublicKey(this._keypair.publicKey)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* The **unencrypted** secret key for this account
|
|
34
|
-
*/
|
|
35
|
-
get secretKey(): Buffer {
|
|
36
|
-
return this._keypair.secretKey
|
|
37
|
-
}
|
|
38
|
-
}
|
package/src/vendor/index.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export * from './account'
|
|
2
|
-
export * from './instruction'
|
|
3
|
-
export * from './message'
|
|
4
|
-
export * from './nonce-account'
|
|
5
|
-
export * from './publickey'
|
|
6
|
-
export * from './system-program'
|
|
7
|
-
export * from './stake-program'
|
|
8
|
-
export * from './sysvar'
|
|
9
|
-
export * from './transaction'
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
|
|
3
|
-
import * as BufferLayout from '@exodus/buffer-layout'
|
|
4
|
-
|
|
5
|
-
import * as Layout from './utils/layout'
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @typedef {Object} InstructionType
|
|
9
|
-
* @property (index} The Instruction index (from solana upstream program)
|
|
10
|
-
* @property (BufferLayout} The BufferLayout to use to build data
|
|
11
|
-
*/
|
|
12
|
-
export type InstructionType = {|
|
|
13
|
-
index: number,
|
|
14
|
-
layout: typeof BufferLayout,
|
|
15
|
-
|}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Populate a buffer of instruction data using an InstructionType
|
|
19
|
-
*/
|
|
20
|
-
export function encodeData(type: InstructionType, fields: Object): Buffer {
|
|
21
|
-
const allocLength = type.layout.span >= 0 ? type.layout.span : Layout.getAlloc(type, fields)
|
|
22
|
-
const data = Buffer.alloc(allocLength)
|
|
23
|
-
const layoutFields = Object.assign({ instruction: type.index }, fields)
|
|
24
|
-
type.layout.encode(layoutFields, data)
|
|
25
|
-
return data
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Decode instruction data buffer using an InstructionType
|
|
30
|
-
*/
|
|
31
|
-
export function decodeData(type: InstructionType, buffer: Buffer): Object {
|
|
32
|
-
let data
|
|
33
|
-
try {
|
|
34
|
-
data = type.layout.decode(buffer)
|
|
35
|
-
} catch (err) {
|
|
36
|
-
throw new Error('invalid instruction; ' + err)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (data.instruction !== type.index) {
|
|
40
|
-
throw new Error(
|
|
41
|
-
`invalid instruction; instruction index mismatch ${data.instruction} != ${type.index}`
|
|
42
|
-
)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return data
|
|
46
|
-
}
|
package/src/vendor/message.js
DELETED
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
|
|
3
|
-
import bs58 from 'bs58'
|
|
4
|
-
import * as BufferLayout from '@exodus/buffer-layout'
|
|
5
|
-
|
|
6
|
-
import { PublicKey } from './publickey'
|
|
7
|
-
import type { Blockhash } from './utils/blockhash'
|
|
8
|
-
import * as Layout from './utils/layout'
|
|
9
|
-
import { PACKET_DATA_SIZE } from './transaction'
|
|
10
|
-
import * as shortvec from './utils/shortvec-encoding'
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* The message header, identifying signed and read-only account
|
|
14
|
-
*
|
|
15
|
-
* @typedef {Object} MessageHeader
|
|
16
|
-
* @property {number} numRequiredSignatures The number of signatures required for this message to be considered valid. The
|
|
17
|
-
* signatures must match the first `numRequiredSignatures` of `accountKeys`.
|
|
18
|
-
* @property {number} numReadonlySignedAccounts: The last `numReadonlySignedAccounts` of the signed keys are read-only accounts
|
|
19
|
-
* @property {number} numReadonlyUnsignedAccounts The last `numReadonlySignedAccounts` of the unsigned keys are read-only accounts
|
|
20
|
-
*/
|
|
21
|
-
export type MessageHeader = {
|
|
22
|
-
numRequiredSignatures: number,
|
|
23
|
-
numReadonlySignedAccounts: number,
|
|
24
|
-
numReadonlyUnsignedAccounts: number,
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* An instruction to execute by a program
|
|
29
|
-
*
|
|
30
|
-
* @typedef {Object} CompiledInstruction
|
|
31
|
-
* @property {number} programIdIndex Index into the transaction keys array indicating the program account that executes this instruction
|
|
32
|
-
* @property {number[]} accounts Ordered indices into the transaction keys array indicating which accounts to pass to the program
|
|
33
|
-
* @property {string} data The program input data encoded as base 58
|
|
34
|
-
*/
|
|
35
|
-
export type CompiledInstruction = {
|
|
36
|
-
programIdIndex: number,
|
|
37
|
-
accounts: number[],
|
|
38
|
-
data: string,
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Message constructor arguments
|
|
43
|
-
*
|
|
44
|
-
* @typedef {Object} MessageArgs
|
|
45
|
-
* @property {MessageHeader} header The message header, identifying signed and read-only `accountKeys`
|
|
46
|
-
* @property {string[]} accounts All the account keys used by this transaction
|
|
47
|
-
* @property {Blockhash} recentBlockhash The hash of a recent ledger block
|
|
48
|
-
* @property {CompiledInstruction[]} instructions Instructions that will be executed in sequence and committed in one atomic transaction if all succeed.
|
|
49
|
-
*/
|
|
50
|
-
type MessageArgs = {
|
|
51
|
-
header: MessageHeader,
|
|
52
|
-
accountKeys: string[],
|
|
53
|
-
recentBlockhash: Blockhash,
|
|
54
|
-
instructions: CompiledInstruction[],
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const PUBKEY_LENGTH = 32
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* List of instructions to be processed atomically
|
|
61
|
-
*/
|
|
62
|
-
export class Message {
|
|
63
|
-
header: MessageHeader
|
|
64
|
-
accountKeys: PublicKey[]
|
|
65
|
-
recentBlockhash: Blockhash
|
|
66
|
-
instructions: CompiledInstruction[]
|
|
67
|
-
|
|
68
|
-
constructor(args: MessageArgs) {
|
|
69
|
-
this.header = args.header
|
|
70
|
-
this.accountKeys = args.accountKeys.map((account) => new PublicKey(account))
|
|
71
|
-
this.recentBlockhash = args.recentBlockhash
|
|
72
|
-
this.instructions = args.instructions
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
isAccountWritable(index: number): boolean {
|
|
76
|
-
return (
|
|
77
|
-
index < this.header.numRequiredSignatures - this.header.numReadonlySignedAccounts ||
|
|
78
|
-
(index >= this.header.numRequiredSignatures &&
|
|
79
|
-
index < this.accountKeys.length - this.header.numReadonlyUnsignedAccounts)
|
|
80
|
-
)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
findSignerIndex(signer: PublicKey): number {
|
|
84
|
-
const index = this.accountKeys.findIndex((accountKey) => {
|
|
85
|
-
return accountKey.equals(signer)
|
|
86
|
-
})
|
|
87
|
-
if (index < 0) {
|
|
88
|
-
throw new Error(`unknown signer: ${signer.toString()}`)
|
|
89
|
-
}
|
|
90
|
-
return index
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
serialize(): Buffer {
|
|
94
|
-
const numKeys = this.accountKeys.length
|
|
95
|
-
|
|
96
|
-
let keyCount = []
|
|
97
|
-
shortvec.encodeLength(keyCount, numKeys)
|
|
98
|
-
|
|
99
|
-
const instructions = this.instructions.map((instruction) => {
|
|
100
|
-
const { accounts, programIdIndex } = instruction
|
|
101
|
-
const data = bs58.decode(instruction.data)
|
|
102
|
-
|
|
103
|
-
let keyIndicesCount = []
|
|
104
|
-
shortvec.encodeLength(keyIndicesCount, accounts.length)
|
|
105
|
-
|
|
106
|
-
let dataCount = []
|
|
107
|
-
shortvec.encodeLength(dataCount, data.length)
|
|
108
|
-
|
|
109
|
-
return {
|
|
110
|
-
programIdIndex,
|
|
111
|
-
keyIndicesCount: Buffer.from(keyIndicesCount),
|
|
112
|
-
keyIndices: Buffer.from(accounts),
|
|
113
|
-
dataLength: Buffer.from(dataCount),
|
|
114
|
-
data,
|
|
115
|
-
}
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
let instructionCount = []
|
|
119
|
-
shortvec.encodeLength(instructionCount, instructions.length)
|
|
120
|
-
let instructionBuffer = Buffer.alloc(PACKET_DATA_SIZE)
|
|
121
|
-
Buffer.from(instructionCount).copy(instructionBuffer)
|
|
122
|
-
let instructionBufferLength = instructionCount.length
|
|
123
|
-
|
|
124
|
-
instructions.forEach((instruction) => {
|
|
125
|
-
const instructionLayout = BufferLayout.struct([
|
|
126
|
-
BufferLayout.u8('programIdIndex'),
|
|
127
|
-
|
|
128
|
-
BufferLayout.blob(instruction.keyIndicesCount.length, 'keyIndicesCount'),
|
|
129
|
-
BufferLayout.seq(BufferLayout.u8('keyIndex'), instruction.keyIndices.length, 'keyIndices'),
|
|
130
|
-
BufferLayout.blob(instruction.dataLength.length, 'dataLength'),
|
|
131
|
-
BufferLayout.seq(BufferLayout.u8('userdatum'), instruction.data.length, 'data'),
|
|
132
|
-
])
|
|
133
|
-
const length = instructionLayout.encode(
|
|
134
|
-
instruction,
|
|
135
|
-
instructionBuffer,
|
|
136
|
-
instructionBufferLength
|
|
137
|
-
)
|
|
138
|
-
instructionBufferLength += length
|
|
139
|
-
})
|
|
140
|
-
instructionBuffer = instructionBuffer.slice(0, instructionBufferLength)
|
|
141
|
-
|
|
142
|
-
const signDataLayout = BufferLayout.struct([
|
|
143
|
-
BufferLayout.blob(1, 'numRequiredSignatures'),
|
|
144
|
-
BufferLayout.blob(1, 'numReadonlySignedAccounts'),
|
|
145
|
-
BufferLayout.blob(1, 'numReadonlyUnsignedAccounts'),
|
|
146
|
-
BufferLayout.blob(keyCount.length, 'keyCount'),
|
|
147
|
-
BufferLayout.seq(Layout.publicKey('key'), numKeys, 'keys'),
|
|
148
|
-
Layout.publicKey('recentBlockhash'),
|
|
149
|
-
])
|
|
150
|
-
|
|
151
|
-
const transaction = {
|
|
152
|
-
numRequiredSignatures: Buffer.from([this.header.numRequiredSignatures]),
|
|
153
|
-
numReadonlySignedAccounts: Buffer.from([this.header.numReadonlySignedAccounts]),
|
|
154
|
-
numReadonlyUnsignedAccounts: Buffer.from([this.header.numReadonlyUnsignedAccounts]),
|
|
155
|
-
keyCount: Buffer.from(keyCount),
|
|
156
|
-
keys: this.accountKeys.map((key) => key.toBuffer()),
|
|
157
|
-
recentBlockhash: bs58.decode(this.recentBlockhash),
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
let signData = Buffer.alloc(2048)
|
|
161
|
-
const length = signDataLayout.encode(transaction, signData)
|
|
162
|
-
instructionBuffer.copy(signData, length)
|
|
163
|
-
return signData.slice(0, length + instructionBuffer.length)
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Decode a compiled message into a Message object.
|
|
168
|
-
*/
|
|
169
|
-
static from(buffer: Buffer | Uint8Array | Array<number>): Message {
|
|
170
|
-
// Slice up wire data
|
|
171
|
-
let byteArray = [...buffer]
|
|
172
|
-
|
|
173
|
-
const numRequiredSignatures = byteArray.shift()
|
|
174
|
-
const numReadonlySignedAccounts = byteArray.shift()
|
|
175
|
-
const numReadonlyUnsignedAccounts = byteArray.shift()
|
|
176
|
-
|
|
177
|
-
const accountCount = shortvec.decodeLength(byteArray)
|
|
178
|
-
let accountKeys = []
|
|
179
|
-
for (let i = 0; i < accountCount; i++) {
|
|
180
|
-
const account = byteArray.slice(0, PUBKEY_LENGTH)
|
|
181
|
-
byteArray = byteArray.slice(PUBKEY_LENGTH)
|
|
182
|
-
accountKeys.push(bs58.encode(Buffer.from(account)))
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const recentBlockhash = byteArray.slice(0, PUBKEY_LENGTH)
|
|
186
|
-
byteArray = byteArray.slice(PUBKEY_LENGTH)
|
|
187
|
-
|
|
188
|
-
const instructionCount = shortvec.decodeLength(byteArray)
|
|
189
|
-
let instructions = []
|
|
190
|
-
for (let i = 0; i < instructionCount; i++) {
|
|
191
|
-
let instruction = {}
|
|
192
|
-
instruction.programIdIndex = byteArray.shift()
|
|
193
|
-
const accountCount = shortvec.decodeLength(byteArray)
|
|
194
|
-
instruction.accounts = byteArray.slice(0, accountCount)
|
|
195
|
-
byteArray = byteArray.slice(accountCount)
|
|
196
|
-
const dataLength = shortvec.decodeLength(byteArray)
|
|
197
|
-
const data = byteArray.slice(0, dataLength)
|
|
198
|
-
instruction.data = bs58.encode(Buffer.from(data))
|
|
199
|
-
byteArray = byteArray.slice(dataLength)
|
|
200
|
-
instructions.push(instruction)
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const messageArgs = {
|
|
204
|
-
header: {
|
|
205
|
-
numRequiredSignatures,
|
|
206
|
-
numReadonlySignedAccounts,
|
|
207
|
-
numReadonlyUnsignedAccounts,
|
|
208
|
-
},
|
|
209
|
-
recentBlockhash: bs58.encode(Buffer.from(recentBlockhash)),
|
|
210
|
-
accountKeys,
|
|
211
|
-
instructions,
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
return new Message(messageArgs)
|
|
215
|
-
}
|
|
216
|
-
}
|