-
Notifications
You must be signed in to change notification settings - Fork 314
Add DeFi examples for contract deployment and interaction #1822
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| # DeFi Examples for Stacks.js | ||
|
|
||
| Real-world examples for building DeFi applications on Stacks blockchain. | ||
|
|
||
| ## Examples | ||
|
|
||
| ### 1. Contract Deployment (`contract-deployment.ts`) | ||
| Deploy Clarity smart contracts programmatically. | ||
|
|
||
| ```bash | ||
| npx ts-node contract-deployment.ts | ||
| ``` | ||
|
|
||
| ### 2. Contract Interaction (`contract-interaction.ts`) | ||
| Call read-only and public functions on deployed contracts. | ||
|
|
||
| ```bash | ||
| npx ts-node contract-interaction.ts | ||
| ``` | ||
|
|
||
| ### 3. Token Operations (`token-operations.ts`) | ||
| Work with SIP-010 fungible tokens - balance, transfer, metadata. | ||
|
|
||
| ```bash | ||
| npx ts-node token-operations.ts | ||
| ``` | ||
|
|
||
| ## Setup | ||
|
|
||
| ```bash | ||
| npm install @stacks/transactions @stacks/network | ||
| ``` | ||
|
|
||
| ## Configuration | ||
|
|
||
| Replace placeholder values: | ||
| - `YOUR_PRIVATE_KEY_HERE` - Your Stacks private key (never commit!) | ||
| - Contract addresses as needed | ||
|
|
||
| ## Network Selection | ||
|
|
||
| ```typescript | ||
| // Testnet | ||
| import { StacksTestnet } from '@stacks/network'; | ||
| const network = new StacksTestnet(); | ||
|
|
||
| // Mainnet | ||
| import { StacksMainnet } from '@stacks/network'; | ||
| const network = new StacksMainnet(); | ||
| ``` | ||
|
|
||
| ## Live Contracts Used in Examples | ||
|
|
||
| These examples use real deployed contracts on mainnet: | ||
|
|
||
| | Contract | Address | | ||
| |----------|---------| | ||
| | sentinel-token | SP2PEBKJ2W1ZDDF2QQ6Y4FXKZEDPT9J9R2NKD9WJB.sentinel-token | | ||
| | voting | SP2PEBKJ2W1ZDDF2QQ6Y4FXKZEDPT9J9R2NKD9WJB.voting | | ||
|
|
||
| ## Resources | ||
|
|
||
| - [Stacks.js Documentation](https://stacks.js.org) | ||
| - [Clarity Language Reference](https://docs.stacks.co/clarity) | ||
| - [Stacks Explorer](https://explorer.stacks.co) | ||
|
|
||
| ## Contributing | ||
|
|
||
| These examples are part of the stacks.js repository. Contributions welcome! | ||
|
|
||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| /** | ||
| * Example: Deploy a Clarity smart contract programmatically | ||
| * | ||
| * This example shows how to deploy a contract to Stacks blockchain | ||
| * using stacks.js libraries. | ||
| */ | ||
|
|
||
| import { | ||
| makeContractDeploy, | ||
| broadcastTransaction, | ||
| AnchorMode, | ||
| PostConditionMode, | ||
| } from '@stacks/transactions'; | ||
| import { StacksMainnet, StacksTestnet } from '@stacks/network'; | ||
|
|
||
| // Contract source code (simple counter example) | ||
| const contractSource = ` | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In real world the contract is either generated or read from file. |
||
| ;; Simple Counter Contract | ||
| (define-data-var counter uint u0) | ||
|
|
||
| (define-public (increment) | ||
| (begin | ||
| (var-set counter (+ (var-get counter) u1)) | ||
| (ok (var-get counter)) | ||
| ) | ||
| ) | ||
|
|
||
| (define-public (decrement) | ||
| (begin | ||
| (var-set counter (- (var-get counter) u1)) | ||
| (ok (var-get counter)) | ||
| ) | ||
| ) | ||
|
|
||
| (define-read-only (get-counter) | ||
| (var-get counter) | ||
| ) | ||
| `; | ||
|
|
||
| async function deployContract() { | ||
| // Configuration | ||
| const network = new StacksTestnet(); // Use StacksMainnet() for mainnet | ||
| const senderKey = 'YOUR_PRIVATE_KEY_HERE'; // Never commit real keys! | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How to never commit keys in real world? |
||
| const contractName = 'my-counter'; | ||
|
|
||
| try { | ||
| // Create the contract deploy transaction | ||
| const transaction = await makeContractDeploy({ | ||
| codeBody: contractSource, | ||
| contractName: contractName, | ||
| senderKey: senderKey, | ||
| network: network, | ||
| anchorMode: AnchorMode.Any, | ||
| postConditionMode: PostConditionMode.Allow, | ||
| fee: 50000n, // 0.05 STX | ||
| }); | ||
|
|
||
| console.log('Transaction created:', transaction.txid()); | ||
|
|
||
| // Broadcast to the network | ||
| const broadcastResponse = await broadcastTransaction({ transaction, network }); | ||
|
|
||
| if ('error' in broadcastResponse) { | ||
| console.error('Broadcast failed:', broadcastResponse.error); | ||
| return; | ||
| } | ||
|
|
||
| console.log('Transaction broadcast successfully!'); | ||
| console.log('TX ID:', broadcastResponse.txid); | ||
| console.log(`Explorer: https://explorer.stacks.co/txid/${broadcastResponse.txid}`); | ||
|
|
||
| return broadcastResponse.txid; | ||
| } catch (error) { | ||
| console.error('Deployment error:', error); | ||
| throw error; | ||
| } | ||
| } | ||
|
|
||
| // Run the deployment | ||
| deployContract() | ||
| .then((txid) => console.log('Deployment initiated:', txid)) | ||
| .catch((err) => console.error('Failed:', err)); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,151 @@ | ||
| /** | ||
| * Example: Interact with deployed Clarity contracts | ||
| * | ||
| * This example demonstrates how to: | ||
| * 1. Call read-only functions | ||
| * 2. Call public functions (state-changing) | ||
| * 3. Handle transaction responses | ||
| */ | ||
|
|
||
| import { | ||
| callReadOnlyFunction, | ||
| makeContractCall, | ||
| broadcastTransaction, | ||
| uintCV, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using Cl is more recommended nowadays because it requires less imports |
||
| stringAsciiCV, | ||
| principalCV, | ||
| cvToJSON, | ||
| ClarityValue, | ||
| AnchorMode, | ||
| } from '@stacks/transactions'; | ||
| import { StacksMainnet, StacksTestnet } from '@stacks/network'; | ||
|
|
||
| // Contract details | ||
| const CONTRACT_ADDRESS = 'SP2PEBKJ2W1ZDDF2QQ6Y4FXKZEDPT9J9R2NKD9WJB'; | ||
| const CONTRACT_NAME = 'voting'; | ||
|
|
||
| const network = new StacksMainnet(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Only name is required |
||
|
|
||
| /** | ||
| * Read-only function call (no gas needed) | ||
| */ | ||
| async function getProposal(proposalId: number) { | ||
| try { | ||
| const result = await callReadOnlyFunction({ | ||
| contractAddress: CONTRACT_ADDRESS, | ||
| contractName: CONTRACT_NAME, | ||
| functionName: 'get-proposal', | ||
| functionArgs: [uintCV(proposalId)], | ||
| network: network, | ||
| senderAddress: CONTRACT_ADDRESS, // Can be any valid address for read-only | ||
| }); | ||
|
|
||
| const jsonResult = cvToJSON(result); | ||
| console.log('Proposal:', jsonResult); | ||
| return jsonResult; | ||
| } catch (error) { | ||
| console.error('Error reading proposal:', error); | ||
| throw error; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Public function call (requires signature and gas) | ||
| */ | ||
| async function createProposal( | ||
| title: string, | ||
| description: string, | ||
| duration: number, | ||
| senderKey: string | ||
| ) { | ||
| try { | ||
| const transaction = await makeContractCall({ | ||
| contractAddress: CONTRACT_ADDRESS, | ||
| contractName: CONTRACT_NAME, | ||
| functionName: 'create-proposal', | ||
| functionArgs: [ | ||
| stringAsciiCV(title), | ||
| stringAsciiCV(description), | ||
| uintCV(duration), | ||
| ], | ||
| senderKey: senderKey, | ||
| network: network, | ||
| anchorMode: AnchorMode.Any, | ||
| fee: 50000n, | ||
| }); | ||
|
|
||
| console.log('Transaction created:', transaction.txid()); | ||
|
|
||
| const broadcastResponse = await broadcastTransaction({ transaction, network }); | ||
|
|
||
| if ('error' in broadcastResponse) { | ||
| throw new Error(`Broadcast failed: ${broadcastResponse.error}`); | ||
| } | ||
|
|
||
| console.log('Proposal created! TX:', broadcastResponse.txid); | ||
| return broadcastResponse.txid; | ||
| } catch (error) { | ||
| console.error('Error creating proposal:', error); | ||
| throw error; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Vote on a proposal | ||
| */ | ||
| async function vote( | ||
| proposalId: number, | ||
| optionId: number, // 0 = Yes, 1 = No | ||
| weight: number, | ||
| senderKey: string | ||
| ) { | ||
| try { | ||
| const transaction = await makeContractCall({ | ||
| contractAddress: CONTRACT_ADDRESS, | ||
| contractName: CONTRACT_NAME, | ||
| functionName: 'vote', | ||
| functionArgs: [ | ||
| uintCV(proposalId), | ||
| uintCV(optionId), | ||
| uintCV(weight), | ||
| ], | ||
| senderKey: senderKey, | ||
| network: network, | ||
| anchorMode: AnchorMode.Any, | ||
| fee: 30000n, | ||
| }); | ||
|
|
||
| const broadcastResponse = await broadcastTransaction({ transaction, network }); | ||
|
|
||
| if ('error' in broadcastResponse) { | ||
| throw new Error(`Vote failed: ${broadcastResponse.error}`); | ||
| } | ||
|
|
||
| console.log('Vote cast! TX:', broadcastResponse.txid); | ||
| return broadcastResponse.txid; | ||
| } catch (error) { | ||
| console.error('Error voting:', error); | ||
| throw error; | ||
| } | ||
| } | ||
|
|
||
| // Example usage | ||
| async function main() { | ||
| // Read proposal (no key needed) | ||
| await getProposal(0); | ||
|
|
||
| // Create proposal (requires private key) | ||
| // const txid = await createProposal( | ||
| // 'Increase Treasury', | ||
| // 'Proposal to increase treasury allocation by 10%', | ||
| // 10080, // ~7 days in blocks | ||
| // 'YOUR_PRIVATE_KEY' | ||
| // ); | ||
|
|
||
| // Vote on proposal | ||
| // await vote(0, 0, 100, 'YOUR_PRIVATE_KEY'); // Vote Yes with weight 100 | ||
| } | ||
|
|
||
| main().catch(console.error); | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Simple counter is not defi and not real world example as mentioned in the readme.