Skip to content

Commit 7b2b921

Browse files
ShantelPetersakintewellama
authored
Enable sac interactions (#35)
* Implement SAC transfer functionality in walletStore * Implement SAC transfer functionality in walletStore * Implement createContractTransferTransaction function * made modifications * modifications * made modifications * changes made * fix a couple imports --------- Co-authored-by: Nathan_akin <85641756+akintewe@users.noreply.github.com> Co-authored-by: llama <llama@llama.fi>
1 parent 1528826 commit 7b2b921

5 files changed

Lines changed: 360 additions & 276 deletions

File tree

src/lib/stellar/transactions.js

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { TransactionBuilder, Networks, Operation, Asset, Memo, Horizon } from '@stellar/stellar-sdk'
1+
import Server from '@stellar/stellar-sdk'
2+
import { TransactionBuilder, Networks, Operation, Asset, Memo, Contract, Horizon, xdr, Address, StrKey, rpc, nativeToScVal } from '@stellar/stellar-sdk'
23
import { error } from '@sveltejs/kit'
3-
44
/**
55
* @module $lib/stellar/transactions
66
* @description A collection of functions that will generate and return
@@ -12,6 +12,7 @@ import { error } from '@sveltejs/kit'
1212
* {@link createChangeTrustTransaction}
1313
* {@link createPathPaymentStrictSendTransaction}
1414
* {@link createPathPaymentStrictReceiveTransaction}
15+
* {@link createContractTransferTransaction}
1516
*/
1617

1718
// We are setting a very high maximum fee, which increases our transaction's
@@ -22,6 +23,7 @@ const maxFeePerOperation = '100000'
2223
const horizonUrl = 'https://horizon-testnet.stellar.org'
2324
const networkPassphrase = Networks.TESTNET
2425
const standardTimebounds = 300 // 5 minutes for the user to review/sign/submit
26+
const rpcUrl = 'https://soroban-testnet.stellar.org'
2527

2628
/**
2729
* For consistency, all functions in this module will return the same type of object.
@@ -185,7 +187,6 @@ export async function createChangeTrustTransaction({ source, asset, limit }) {
185187
network_passphrase: networkPassphrase,
186188
}
187189
}
188-
189190
/**
190191
* Constructs and returns a Stellar transaction that will contain a path payment strict send operation to send/receive different assets.
191192
* @async
@@ -331,3 +332,47 @@ export async function createPathPaymentStrictReceiveTransaction({
331332
network_passphrase: networkPassphrase,
332333
}
333334
}
335+
336+
/**
337+
* Constructs and returns a Stellar transaction for transferring assets to a contract or account.
338+
* @async
339+
* @function createContractTransferTransaction
340+
* @param {Object} opts Options object
341+
* @param {string} opts.source Public Stellar address to use as the source account of the transaction
342+
* @param {string} opts.destination Public Stellar address or contract ID to receive the transfer
343+
* @param {string} opts.amount Amount of the asset to transfer
344+
* @param {string} opts.asset Asset to be transferred (example: USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5)
345+
* @returns {Promise<TransactionResponse>} Object containing the relevant network passphrase and the built transaction envelope in XDR base64 encoding, ready to be signed and submitted
346+
*/
347+
export async function createContractTransferTransaction({ source, destination, amount, asset }) {
348+
const server = new rpc.Server(rpcUrl)
349+
const sourceAccount = await server.getAccount(source)
350+
351+
const transaction = new TransactionBuilder(sourceAccount, {
352+
networkPassphrase: networkPassphrase,
353+
fee: maxFeePerOperation,
354+
});
355+
356+
const [assetCode, assetIssuer] = asset.split(':');
357+
const contractId = new Asset(assetCode, assetIssuer).contractId(networkPassphrase);
358+
const contract = new Contract(contractId);
359+
360+
const transferOp = contract.call(
361+
"transfer",
362+
nativeToScVal(source, { type: 'address' }),
363+
nativeToScVal(destination, { type: 'address' }),
364+
nativeToScVal(amount, { type: 'i128' })
365+
)
366+
transaction.addOperation(transferOp);
367+
368+
const builtTransaction = transaction.setTimeout(standardTimebounds).build();
369+
370+
// Simulate the transaction
371+
const rpcServer = new rpc.Server(rpcUrl);
372+
const simulatedTx = await server.prepareTransaction(builtTransaction)
373+
374+
return {
375+
transaction: simulatedTx.toXDR(),
376+
network_passphrase: networkPassphrase,
377+
};
378+
}

src/lib/stores/contactsStore.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,14 @@ function createContactsStore() {
4545
* Adds a new contact entry to the list with the provided details.
4646
* @function add
4747
* @param {ContactEntry} contact Details of new contact entry to add to the list
48-
* @throws Will throw an error if the new contact entry contains an invalid public key in the `address` field
48+
* @throws Will throw an error if the new contact entry contains an invalid public key or contract address in the `address` field
4949
*/
5050
add: (contact) =>
5151
update((list) => {
5252
if (StrKey.isValidEd25519PublicKey(contact.address) || StrKey.isValidContract(contact.address)) {
5353
return [...list, { ...contact, id: uuidv4() }]
5454
} else {
55-
throw error(400, { message: 'invalid public key' })
55+
throw error(400, { message: 'invalid public key or contract address' })
5656
}
5757
}),
5858

@@ -83,6 +83,15 @@ function createContactsStore() {
8383
return false
8484
}
8585
},
86+
87+
/**
88+
* Checks if the given address is a contract address
89+
* @param {string} address Address to check
90+
* @returns {boolean} True if the address is a contract address, false otherwise
91+
*/
92+
isContractAddress: (address) => {
93+
return StrKey.isValidContract(address)
94+
},
8695
}
8796
}
8897

src/lib/stores/walletStore.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,4 @@ const setupKeyManager = () => {
129129
keyManager.registerEncrypter(ScryptEncrypter)
130130

131131
return keyManager
132-
}
132+
}

src/routes/dashboard/send/+page.svelte

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ features have been implemented:
4444
createPathPaymentStrictReceiveTransaction,
4545
createPathPaymentStrictSendTransaction,
4646
createPaymentTransaction,
47+
createContractTransferTransaction,
4748
} from '$lib/stellar/transactions'
4849
4950
// The `open` Svelte context is used to open the confirmation modal
@@ -62,7 +63,7 @@ features have been implemented:
6263
/** @type {boolean|null} */
6364
let createAccount = null
6465
let pathPayment = false
65-
/** @type {import('stellar-sdk').ServerApi.PaymentPathRecord[]} */
66+
/** @type {import('@stellar/stellar-sdk').Horizon.ServerApi.PaymentPathRecord[]} */
6667
let availablePaths = []
6768
let strictReceive = false
6869
let paymentXDR = ''
@@ -175,19 +176,27 @@ features have been implemented:
175176
* @function previewPaymentTransaction
176177
*/
177178
const previewPaymentTransaction = async () => {
179+
const destinationAddress = otherDestination ? otherPublicKey : destination;
178180
let { transaction, network_passphrase } = createAccount
179181
? await createCreateAccountTransaction({
180182
source: data.publicKey,
181-
destination: otherDestination ? otherPublicKey : destination,
183+
destination: destinationAddress,
182184
amount: sendAmount,
183185
memo: memo,
184186
})
187+
: contacts.isContractAddress(destinationAddress)
188+
? await createContractTransferTransaction({
189+
source: data.publicKey,
190+
destination: destinationAddress,
191+
asset: sendAsset,
192+
amount: sendAmount,
193+
})
185194
: pathPayment && strictReceive
186195
? await createPathPaymentStrictReceiveTransaction({
187196
source: data.publicKey,
188197
sourceAsset: sendAsset,
189198
sourceAmount: sendAmount,
190-
destination: otherDestination ? otherPublicKey : destination,
199+
destination: destinationAddress,
191200
destinationAsset: receiveAsset,
192201
destinationAmount: receiveAmount,
193202
memo: memo,
@@ -197,22 +206,22 @@ features have been implemented:
197206
source: data.publicKey,
198207
sourceAsset: sendAsset,
199208
sourceAmount: sendAmount,
200-
destination: otherDestination ? otherPublicKey : destination,
209+
destination: destinationAddress,
201210
destinationAsset: receiveAsset,
202211
destinationAmount: receiveAmount,
203212
memo: memo,
204213
})
205214
: await createPaymentTransaction({
206215
source: data.publicKey,
207-
destination: otherDestination ? otherPublicKey : destination,
216+
destination: destinationAddress,
208217
asset: sendAsset,
209218
amount: sendAmount,
210219
memo: memo,
211-
})
220+
});
212221
213222
// Set the component variables to hold the transaction details
214-
paymentXDR = transaction
215-
paymentNetwork = network_passphrase
223+
paymentXDR = transaction;
224+
paymentNetwork = network_passphrase;
216225
217226
// Open the confirmation modal for the user to confirm or reject the
218227
// transaction. We provide our customized `onConfirm` function, but we
@@ -221,7 +230,7 @@ features have been implemented:
221230
transactionXDR: paymentXDR,
222231
transactionNetwork: paymentNetwork,
223232
onConfirm: onConfirm,
224-
})
233+
});
225234
}
226235
</script>
227236

@@ -464,7 +473,6 @@ features have been implemented:
464473

465474
<!-- Button -->
466475
<div class="form-control my-5">
467-
<button class="btn-primary btn" on:click={previewPaymentTransaction}>Preview Transaction</button
468-
>
476+
<button class="btn-primary btn" on:click={previewPaymentTransaction}>Preview Transaction</button>
469477
</div>
470478
<!-- /Button -->

0 commit comments

Comments
 (0)