diff --git a/src/lib/stores/contractStore.js b/src/lib/stores/contractStore.js new file mode 100644 index 0000000..4a48953 --- /dev/null +++ b/src/lib/stores/contractStore.js @@ -0,0 +1,100 @@ +import { persisted } from 'svelte-local-storage-store'; +import { StrKey } from '@stellar/stellar-sdk'; +import { get } from 'svelte/store'; + +/** + * @typedef {Object} SavedContract + * @property {string} contractId - The Stellar contract ID + * @property {string} name - Human-readable name for this contract + * @property {string} [description] - Optional description of the contract + */ + +/** + * @typedef {Object} ContractStore + * @property {SavedContract[]} savedContracts - An array of saved contracts + * @property {SavedContract|null} currentContract - The currently active contract or null + */ + +function createContractStore() { + /** + * @type {import('svelte/store').Writable} + */ + const { subscribe, set, update } = persisted('bpa:contractStore', { + savedContracts: [], + currentContract: null + }); + + return { + subscribe, + + /** + * Saves a new contract to the store + * @param {SavedContract} contract - Contract details to save + * @throws Will throw an error if the contract ID is invalid + */ + saveContract: (contract) => + update(store => { + if (!StrKey.isValidContract(contract.contractId)) { + throw new Error('Invalid contract ID'); + } + + const newContract = { ...contract }; + const updatedStore = { + ...store, + savedContracts: [...store.savedContracts, newContract] + }; + + // Log a success message to the console + console.log('Contract saved successfully:', newContract); + + return updatedStore; + }), + + /** + * Removes a contract from the store + * @param {string} id - Unique identifier of the contract to remove + */ + removeContract: (id) => + update(store => ({ + ...store, + savedContracts: store.savedContracts.filter(c => c.contractId !== id), + currentContract: store.currentContract?.contractId === id ? null : store.currentContract + })), + + /** + * Sets the current active contract using its ID + * @param {string|null} contractId - Contract ID to set as current, or null to clear + * @throws Will throw an error if the contract ID is not found in saved contracts + */ + setCurrentContract: (contractId) => + update(store => { + if (contractId === null) { + return { ...store, currentContract: null }; + } + + const contract = store.savedContracts.find(c => c.contractId === contractId); + if (!contract) { + throw new Error('Contract not found'); + } + + return { ...store, currentContract: contract }; + }), + + /** + * Looks up a contract by its Stellar contract ID + * @param {string} contractId - Stellar contract ID to look up + * @returns {SavedContract|undefined} The found contract or undefined + */ + lookup: (contractId) => { + const store = get(contractStore); + return store.savedContracts.find(contract => contract.contractId === contractId); + }, + + /** + * Clears all saved contracts from the store + */ + empty: () => set({ savedContracts: [], currentContract: null }) + }; +} + +export const contractStore = createContractStore(); \ No newline at end of file diff --git a/src/lib/utils/contractUtils.js b/src/lib/utils/contractUtils.js new file mode 100644 index 0000000..4ebb564 --- /dev/null +++ b/src/lib/utils/contractUtils.js @@ -0,0 +1,56 @@ +import { StrKey, Contract } from '@stellar/stellar-sdk'; +import { Server } from '@stellar/stellar-sdk/rpc'; + +/** + * Determines if a contract ID represents a Stellar Asset Contract (SAC) + * @param {string} contractId - The contract ID to check + * @returns {boolean} True if the contract ID is for a SAC + */ +function isStellarAssetContract(contractId) { + return contractId.startsWith('CA'); +} + +/** + * Generates a contract client for interacting with a Stellar smart contract + * @param {string} contractId - The contract ID to validate and connect to + * @returns {Promise} A Contract instance + */ +export async function generateContractClient(contractId) { + const server = new Server('https://soroban-testnet.stellar.org'); + + // Validate contract ID + if (!StrKey.isValidContract(contractId)) { + throw new Error('Invalid contract ID format'); + } + + try { + if (isStellarAssetContract(contractId)) { + // For SAC contracts, create contract instance without fetching WASM + const contract = new Contract(contractId); + console.log('Contract:', contract); + return contract; + } + else { + // For WASM contracts, fetch the WASM first + console.log('Fetching WASM for contract ID:', contractId); + const contractResponse = await server.getContractWasmByContractId(contractId); + + console.log('Contract WASM length:', contractResponse.length); + const contract = new Contract(contractId); + console.log(contract); + + // You might want to add the contract's interface here + // This could involve parsing the WASM to get available methods + + return contract; + } + } catch (serverError) { + console.error('Error handling contract:', serverError); + + if (serverError.response && serverError.response.status === 404) { + throw new Error(`Contract not found: ${contractId}`); + } + + throw new Error(`Failed to handle contract: ${serverError.message}`); + } +} \ No newline at end of file diff --git a/src/routes/dashboard/components/SidebarMenu.svelte b/src/routes/dashboard/components/SidebarMenu.svelte index c172e89..721a8af 100644 --- a/src/routes/dashboard/components/SidebarMenu.svelte +++ b/src/routes/dashboard/components/SidebarMenu.svelte @@ -17,6 +17,7 @@ list of menu links that can be used to navigate throughout the dashboard. { route: '/dashboard/assets', text: 'Assets' }, { route: '/dashboard/contacts', text: 'Contacts' }, { route: '/dashboard/transfers', text: 'Transfers' }, + { route: '/dashboard/contracts', text: 'Smart Contracts' } ] diff --git a/src/routes/dashboard/contracts/+page.svelte b/src/routes/dashboard/contracts/+page.svelte new file mode 100644 index 0000000..6508bb8 --- /dev/null +++ b/src/routes/dashboard/contracts/+page.svelte @@ -0,0 +1,90 @@ + + +
+

Stellar Smart Contract Interaction

+ +
+ + +
+ +
+ + +
+ + + + {#if error} +
{error}
+ {/if} + +

Saved Contracts

+ {#each $contractStore.savedContracts as savedContract} +
+
+ {savedContract.name || 'Unnamed Contract'} + {savedContract.contractId} +
+ +
+ {/each} +