@stellar-broker/client 0.4.1 → 0.6.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.
@@ -1,162 +1,134 @@
1
- import {hash, Keypair, nativeToScVal, StrKey, TransactionBuilder, xdr} from '@stellar/stellar-sdk'
2
- import errors from './errors.js'
3
-
4
- /**
5
- * @param {StellarBrokerClient} client
6
- * @param {{xdr: string, hash: string, networkFee: string, [confirmed]: boolean}} txRequest
7
- * @return {Promise<string>}
8
- */
9
- export async function processTxRequest(client, txRequest) {
10
- //parse incoming transaction
11
- let tx
12
- try {
13
- tx = TransactionBuilder.fromXDR(txRequest.xdr, client.network)
14
- } catch (e) {
15
- throw errors.invalidSwapTx()
16
- }
17
- //check that transaction is correct
18
- if (!validateTransaction(client, tx))
19
- throw errors.invalidSwapTx()
20
- //if transaction has not been authorized yet by the channel account
21
- const isSorobanTx = tx.operations[0].auth?.length > 0
22
- if (isSorobanTx) {
23
- //send tx back to the server
24
- if (!tx.signatures.length) {
25
- //sign auth
26
- await authorizeInvocation(client, tx)
27
- tx = await signTx(client, tx)
28
- return tx.toXDR()//wait for the signed tx from the server to wrap it with fee bump tx
29
- }
30
- } else {
31
- //sign transaction
32
- tx = await signTx(client, tx)
33
- }
34
- //wrap with fee bump
35
- let wrapped = TransactionBuilder.buildFeeBumpTransaction(client.trader, txRequest.networkFee, tx, client.network)
36
- //sign fee bump wrapper tx
37
- wrapped = await signTx(client, wrapped)
38
- //respond with signed transaction
39
- return wrapped.toXDR()
40
- }
41
-
42
- /**
43
- * @param {StellarBrokerClient} client
44
- * @param {TransactionI} tx
45
- * @return {Promise<TransactionI>}
46
- * @private
47
- */
48
- async function authorizeInvocation(client, tx) {
49
- //from https://github.com/stellar/js-stellar-base/blob/e3d6fc3351e7d242b374c7c6057668366364a279/src/auth.js#L97
50
- const auth = tx.operations[0].auth[0]
51
- /** @type {xdr.SorobanAddressCredentials} */
52
- const addrAuth = auth.credentials().address()
53
- addrAuth.signatureExpirationLedger(tx.ledgerBounds.maxLedger + 1)
54
-
55
- const networkId = hash(Buffer.from(tx.networkPassphrase))
56
- const preimage = xdr.HashIdPreimage.envelopeTypeSorobanAuthorization(
57
- new xdr.HashIdPreimageSorobanAuthorization({
58
- networkId,
59
- nonce: addrAuth.nonce(),
60
- invocation: auth.rootInvocation(),
61
- signatureExpirationLedger: addrAuth.signatureExpirationLedger()
62
- })
63
- )
64
- const payload = hash(preimage.toXDR())
65
- let signature
66
- if (client.authorization instanceof Keypair) {
67
- signature = client.authorization.sign(payload)
68
- } else {
69
- signature = await promisify(client.authorization(payload))
70
- }
71
- const publicKey = StrKey.encodeEd25519PublicKey(addrAuth.address().accountId().value())
72
- const sigScVal = nativeToScVal(
73
- {
74
- public_key: StrKey.decodeEd25519PublicKey(publicKey),
75
- signature
76
- },
77
- {
78
- type: {
79
- public_key: ['symbol', null],
80
- signature: ['symbol', null]
81
- }
82
- }
83
- )
84
-
85
- addrAuth.signature(xdr.ScVal.scvVec([sigScVal]))
86
- }
87
-
88
- /**
89
- * @param {StellarBrokerClient} client
90
- * @param {TransactionI} tx
91
- * @return {Promise<TransactionI>}
92
- * @private
93
- */
94
- async function signTx(client, tx) {
95
- let promise
96
- if (client.authorization instanceof Keypair) {
97
- promise = new Promise((resolve, reject) => {
98
- try {
99
- tx.sign(client.authorization)
100
- resolve(tx)
101
- } catch (e) {
102
- reject(e)
103
- }
104
- })
105
- } else {
106
- //normalize external auth result
107
- promise = promisify(client.authorization(tx))
108
- }
109
- return promise
110
- .then(tx => {
111
- if (typeof tx === 'string') { //normalize response
112
- tx = TransactionBuilder.fromXDR(tx, client.network)
113
- }
114
- if (!tx || tx.signatures?.length < 1)
115
- throw new Error(`Transaction was not signed`)
116
- return tx
117
- })
118
- .catch(e => {
119
- console.error(e)
120
- throw errors.failedToSignTx()
121
- })
122
- }
123
-
124
- /**
125
- * @param {StellarBrokerClient} client
126
- * @param {Transaction} tx
127
- * @throws {StellarBrokerError} Invalid swap transaction received
128
- */
129
- function validateTransaction(client, tx) {
130
- /*if (!(tx instanceof FeeBumpTransaction))
131
- return false
132
- const {innerTransaction} = tx
133
- if (tx.feeSource !== this.source)
134
- return false*/
135
- for (let swap of tx.operations) {
136
- if (swap.type === 'invokeHostFunction')
137
- continue //TODO: add additional validation
138
- if (swap.type !== 'pathPaymentStrictSend' && swap.type !== 'pathPaymentStrictReceive')
139
- return false
140
- const isFee = swap.destination !== client.trader
141
- if (isFee) {
142
- if (swap.type !== 'pathPaymentStrictSend')
143
- return false
144
- if ((swap.source && swap.source !== client.trader))
145
- return false
146
- } else {
147
- if ((swap.source && swap.source !== swap.destination) || swap.destination !== client.trader)
148
- return false
149
- }
150
- }
151
- //TODO: check assets and amounts
152
- return true
153
- }
154
-
155
- function promisify(callResult) {
156
- if (!(callResult instanceof Promise)) {
157
- if (!callResult)
158
- return Promise.reject()
159
- callResult = Promise.resolve(callResult)
160
- }
161
- return callResult
1
+ import {hash, Keypair, nativeToScVal, StrKey, TransactionBuilder, xdr} from '@stellar/stellar-sdk'
2
+ import errors from './errors.js'
3
+
4
+ /**
5
+ * @param {StellarBrokerClient} client
6
+ * @param {{xdr: string, hash: string, networkFee: string, [confirmed]: boolean}} txRequest
7
+ * @return {Promise<string>}
8
+ */
9
+ export async function processTxRequest(client, txRequest) {
10
+ //parse incoming transaction
11
+ let tx
12
+ try {
13
+ tx = TransactionBuilder.fromXDR(txRequest.xdr, client.network)
14
+ } catch (e) {
15
+ throw errors.invalidSwapTx()
16
+ }
17
+ //check that transaction is correct
18
+ if (!validateTransaction(client, tx))
19
+ throw errors.invalidSwapTx()
20
+ //if transaction has not been authorized yet by the channel account
21
+ const isSorobanTx = tx.operations[0].auth?.length > 0
22
+ if (isSorobanTx) {
23
+ //send tx back to the server
24
+ if (!tx.signatures.length) {
25
+ //sign auth
26
+ await authorizeInvocation(client, tx)
27
+ tx = await signTx(client, tx)
28
+ return tx.toXDR()//wait for the signed tx from the server to wrap it with fee bump tx
29
+ }
30
+ } else {
31
+ //sign transaction
32
+ tx = await signTx(client, tx)
33
+ }
34
+ //wrap with fee bump
35
+ let wrapped = TransactionBuilder.buildFeeBumpTransaction(client.trader, txRequest.networkFee, tx, client.network)
36
+ //sign fee bump wrapper tx
37
+ wrapped = await signTx(client, wrapped)
38
+ //respond with signed transaction
39
+ return wrapped.toXDR()
40
+ }
41
+
42
+ /**
43
+ * @param {StellarBrokerClient} client
44
+ * @param {TransactionI} tx
45
+ * @return {Promise<TransactionI>}
46
+ * @private
47
+ */
48
+ async function authorizeInvocation(client, tx) {
49
+ //from https://github.com/stellar/js-stellar-base/blob/e3d6fc3351e7d242b374c7c6057668366364a279/src/auth.js#L97
50
+ const auth = tx.operations[0].auth[0]
51
+ /** @type {xdr.SorobanAddressCredentials} */
52
+ const addrAuth = auth.credentials().address()
53
+ addrAuth.signatureExpirationLedger(tx.ledgerBounds.maxLedger + 1)
54
+
55
+ const networkId = hash(Buffer.from(tx.networkPassphrase))
56
+ const preimage = xdr.HashIdPreimage.envelopeTypeSorobanAuthorization(
57
+ new xdr.HashIdPreimageSorobanAuthorization({
58
+ networkId,
59
+ nonce: addrAuth.nonce(),
60
+ invocation: auth.rootInvocation(),
61
+ signatureExpirationLedger: addrAuth.signatureExpirationLedger()
62
+ })
63
+ )
64
+ const payload = hash(preimage.toXDR())
65
+ const signature = await client.authorization.authorize(payload)
66
+ const publicKey = StrKey.encodeEd25519PublicKey(addrAuth.address().accountId().value())
67
+ const sigScVal = nativeToScVal(
68
+ {
69
+ public_key: StrKey.decodeEd25519PublicKey(publicKey),
70
+ signature
71
+ },
72
+ {
73
+ type: {
74
+ public_key: ['symbol', null],
75
+ signature: ['symbol', null]
76
+ }
77
+ }
78
+ )
79
+
80
+ addrAuth.signature(xdr.ScVal.scvVec([sigScVal]))
81
+ }
82
+
83
+ /**
84
+ * @param {StellarBrokerClient} client
85
+ * @param {TransactionI} tx
86
+ * @return {Promise<TransactionI>}
87
+ * @private
88
+ */
89
+ function signTx(client, tx) {
90
+ return client.authorization.authorize(tx)
91
+ .then(tx => {
92
+ if (typeof tx === 'string') { //normalize response
93
+ tx = TransactionBuilder.fromXDR(tx, client.network)
94
+ }
95
+ if (!tx || tx.signatures?.length < 1)
96
+ throw new Error(`Transaction was not signed`)
97
+ return tx
98
+ })
99
+ .catch(e => {
100
+ console.error(e)
101
+ throw errors.failedToSignTx()
102
+ })
103
+ }
104
+
105
+ /**
106
+ * @param {StellarBrokerClient} client
107
+ * @param {Transaction} tx
108
+ * @throws {StellarBrokerError} Invalid swap transaction received
109
+ */
110
+ function validateTransaction(client, tx) {
111
+ /*if (!(tx instanceof FeeBumpTransaction))
112
+ return false
113
+ const {innerTransaction} = tx
114
+ if (tx.feeSource !== this.source)
115
+ return false*/
116
+ for (let swap of tx.operations) {
117
+ if (swap.type === 'invokeHostFunction')
118
+ continue //TODO: add additional validation
119
+ if (swap.type !== 'pathPaymentStrictSend' && swap.type !== 'pathPaymentStrictReceive')
120
+ return false
121
+ const isFee = swap.destination !== client.trader
122
+ if (isFee) {
123
+ if (swap.type !== 'pathPaymentStrictSend')
124
+ return false
125
+ if ((swap.source && swap.source !== client.trader))
126
+ return false
127
+ } else {
128
+ if ((swap.source && swap.source !== swap.destination) || swap.destination !== client.trader)
129
+ return false
130
+ }
131
+ }
132
+ //TODO: check assets and amounts
133
+ return true
162
134
  }