PageSourceSearch

https://docs.milkomeda.com/assets/js/a35a521a.2d046b94.js

js milkomeda.com collected 2026-09-28 07:35:03 UTC 12,513 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunkmilkomeda_documentation=self.webpackChunkmilkomeda_documentation||[]).push([[9009],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>m});var a=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?i(Object(n),!0).forEach((function(t){r(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):i(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,a,r=function(e,t){if(null==e)return{};var n,a,r={},i=Object.keys(e);for(a=0;a<i.length;a++)n=i[a],t.indexOf(n)>=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a<i.length;a++)n=i[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var s=a.createContext({}),c=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},d=function(e){var t=c(e.components);return a.createElement(s.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},p=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,i=e.originalType,s=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),p=c(n),m=r,f=p["".concat(s,".").concat(m)]||p[m]||u[m]||i;return n?a.createElement(f,o(o({ref:t},d),{},{components:n})):a.createElement(f,o({ref:t},d))}));function m(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=n.length,o=new Array(i);o[0]=p;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:r,o[1]=l;for(var c=2;c<i;c++)o[c]=n[c];return a.createElement.apply(null,o)}return a.createElement.apply(null,n)}p.displayName="MDXCreateElement"},7504:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>u,frontMatter:()=>i,metadata:()=>l,toc:()=>c});var a=n(7462),r=(n(7294),n(3905));const i={sidebar_position:3},o="Integrating Liquid Staking",l={unversionedId:"cardano/liquid-staking/for_developers/integrating",id:"cardano/liquid-staking/for_developers/integrating",title:"Integrating Liquid Staking",description:"Milkomeda Liquid Staking can be integrated in any EVM DApp by supporting the stMADA token, since rewards will accrue to the smart contract that hold stMADA.",source:"@site/docs/cardano/liquid-staking/for_developers/integrating.md",sourceDirName:"cardano/liquid-staking/for_developers",slug:"/cardano/liquid-staking/for_developers/integrating",permalink:"/cardano/liquid-staking/for_developers/integrating",draft:!1,tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3},sidebar:"cardano",previous:{title:"Protocol Components",permalink:"/cardano/liquid-staking/for_developers/"},next:{title:"Deployed Contracts",permalink:"/cardano/liquid-staking/for_developers/deployed_contracts"}},s={},c=[{value:"Example: <code>RewardsAccruer</code> Smart Contract",id:"example-rewardsaccruer-smart-contract",level:3},{value:"Deployment",id:"deployment",level:4}],d={toc:c};
1function u(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,a.Z)({},d,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"integrating-liquid-staking"},"Integrating Liquid Staking"),(0,r.kt)("p",null,"Milkomeda Liquid Staking can be integrated in any EVM DApp by supporting the stMADA token, since rewards will accrue to the smart contract that hold stMADA."),(0,r.kt)("p",null,"As a simple example, consider a smart contract where anyone can deposit any amount of stMADA, which will then accumulate rewards. This smart contract will also contain a withdraw function and the first person to call this function will receive all the amount deposited plus rewards accrued."),(0,r.kt)("h3",{id:"example-rewardsaccruer-smart-contract"},"Example: ",(0,r.kt)("inlineCode",{parentName:"h3"},"RewardsAccruer")," Smart Contract"),(0,r.kt)("p",null,"We will call this smart contract ",(0,r.kt)("em",{parentName:"p"},"RewardsAccruer"),", an we will initialize it with the address of the stMADA token (proxy) contract and the LiquidStaking (proxy) contract."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-solidity"},"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.9;\n\ncontract RewardsAccruer {\n    address public stMADA;\n    address public liquidStaking;\n\n    constructor(address _stMADA, address _liquidStaking) {\n        stMADA = _stMADA;\n        liquidStaking = _liquidStaking;\n    }\n}\n")),(0,r.kt)("p",null,"To interact with the stMADA and LiquidStaking contracts we will also add the required interfaces."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-solidity"},"interface ISTMADA {\n    function transferFrom(\n        address _from,\n        address _to,\n        uint256 _amount\n    ) external returns (bool);\n\n    function transfer(address _to, uint256 _amount) external returns (bool);\n    function balanceOf(address _account) external view returns (uint256);\n}\n\ninterface ILiquidStaking {\n    function withdrawRewards() external returns (uint256);\n    function rewards(address _account) external view returns (uint256);\n}\n")),(0,r.kt)("p",null,"The deposit function will be very simple as the contract won't store individual balances."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-solidity"},"    function deposit(uint256 _amount) public payable {\n        ISTMADA(stMADA).transferFrom(msg.sender, address(this), _amount);\n    }\n")),(0,r.kt)("p",null,"Then we'll add a function for the smart contract to claim it's rewards, and a withdraw function.\nCalling the withdraw function will return to the caller all the stMADA available in the smart contract, which includes the accrued rewards for the contract's shares."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-solidity"},"    function claim() public {\n        ILiquidStaking(liquidStaking).withdrawRewards();\n    }\n\n    function withdraw() public returns (bool) {\n        claim();\n        uint256 totalAmount = ISTMADA(stMADA).balanceOf(address(this));\n        return ISTMADA(stMADA).transfer(msg.sender, totalAmount);\n    }\n")),(0,r.kt)("p",null,"Finally, in order to flag to the Milkomeda DAO that the smart contract is able to claim rewards, we'll add the ",(0,r.kt)("inlineCode",{parentName:"p"},"ableToWithdrawRewards")," that will return ",(0,r.kt)("inlineCode",{parentName:"p"},"true"),". Check ",(0,r.kt)("a",{parentName:"p",href:"/cardano/liquid-staking/for_developers/#claiming-rewards"},"claim rewards")," section for more details."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-solidity"},"function ableToWithdrawRewards() external pure returns (bool) {\n    return true;\n}\n")),(0,r.kt)("details",null,(0,r.kt)("summary",null,"Full code"),(0,r.kt)("p",null,(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-solidity"},"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.9;\n\ninterface ISTMADA {\n    function transferFrom(\n        address _from,\n        address _to,\n        uint256 _amount\n    ) external returns (bool);\n\n    function transfer(address _to, uint256 _amount) external returns (bool);\n    function balanceOf(address _account) external view returns (uint256);\n}\n\ninterface ILiquidStaking {\n    function withdrawRewards() external returns (uint256);\n    function rewards(address _account) external view returns (uint256);\n}\n\ncontract RewardsAccruer {\n    address public stMADA;\n    address public liquidStaking;\n\n    constructor(address _stMADA, address _liquidStaking) {\n        stMADA = _stMADA;\n        liquidStaking = _liquidStaking;\n    }\n\n    function deposit(uint256 _amount) public payable {\n        ISTMADA(stMADA).transferFrom(msg.sender, address(this), _amount);\n    }\n\n    function claim() public {\n        ILiquidStaking(liquidStaking).withdrawRewards();\n    }\n\n    function withdraw() public returns (bool) {\n        claim();\n        uint256 totalAmount = ISTMADA(stMADA).balanceOf(address(this));\n        return ISTMADA(stMADA).transfer(msg.sender, totalAmount);\n    }\n}\n\n")))),(0,r.kt)("h4",{id:"deployment"},"Deployment"),(0,r.kt)("p",null,"We will use ",(0,r.kt)("a",{parentName:"p",href:"https://book.getfoundry.sh/reference/forge/forge-create"},"forge")," from the Foundry is a smart contract development toolchain to deploy our smart contract."),(0,r.kt)("p",null,"First, let's creating a ",(0,r.kt)("inlineCode",{parentName:"p"},".env")," file to store some variables:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"}
1,"RPC_URL=https://rpc-devnet-cardano-evm.c1.milkomeda.com\nSTMADA_ADDRESS=0xE42f60abfC51c9620706b69aEEEf838D25120150\nLIQUID_STAKING_ADDRESS=0x45f48842ef34da79a5b426f427e63d9fa8b53696\nPRIVATE_KEY=<PRIVATE_KEY>\nDEPLOYED_CONTRACT=\n")),(0,r.kt)("p",null,"The RPC url is pointing to ",(0,r.kt)("a",{parentName:"p",href:"/cardano/for-developers/overview"},"Devnet")," and for the contstructor arguments we'll use the ",(0,r.kt)("a",{parentName:"p",href:"/cardano/liquid-staking/for_developers/deployed_contracts"},"addresses")," of stMADA token and the LiquidStaking contract. The private key should be for an address that has mADA to be able to deploy and interact with the smart contract."),(0,r.kt)("p",null,"Load the variables into the environment by running:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"source .env\n")),(0,r.kt)("p",null,"Now we can deploy the smart contract:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"forge create src/tutorial/RewardsAccruer.sol:RewardsAccruer\\\n --legacy\\\n --rpc-url $RPC_URL\\\n --private-key $PRIVATE_KEY\\\n --constructor-args $STMADA_ADDRESS $LIQUID_STAKING_ADDRESS\n")),(0,r.kt)("p",null,"(Notice the ",(0,r.kt)("inlineCode",{parentName:"p"},"--legacy")," flag is used since Milkomeda C1 mainnet and devnet don't support EIP-1559)"),(0,r.kt)("p",null,"After deployment, save the contract address to the ",(0,r.kt)("inlineCode",{parentName:"p"},"DEPLOYED_CONTRACT")," variable in the ",(0,r.kt)("inlineCode",{parentName:"p"},".env")," file and load it again."),(0,r.kt)("p",null,"To test the new deployed contrct, let's make a deposit, using forge's ",(0,r.kt)("a",{parentName:"p",href:"https://book.getfoundry.sh/reference/cast/cast"},"cast"),". The first command will approve spending the stMADA by the newly deployed contract, and the second will make a deposit of 1 stMADA."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'cast send $STMADA_ADDRESS\\\n "approve(address,uint256)(bool)" $DEPLOYED_CONTRACT 1ether\\\n --rpc-url $RPC_URL\\\n --legacy --private-key $PRIVATE_KEY\n\ncast send $DEPLOYED_CONTRACT\\\n "deposit(uint256)()" 1ether\\\n  --rpc-url $RPC_URL\\\n  --legacy --private-key $PRIVATE_KEY\n')),(0,r.kt)("p",null,"Now we can check the stMADA balance of the smart contract, which should output 1000000000000000000 (1 stMADA) after one inicial deposit."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'cast call $STMADA_ADDRESS "balanceOf(address)(uint256)" $DEPLOYED_CONTRACT --rpc-url $RPC_URL\n')),(0,r.kt)("p",null,"We can also check the unclaimed rewards for the deployed smart contract in the Liquid staking contract. It should be zero until some ADA rewards are deposited distributed."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'cast call $LIQUID_STAKING_ADDRESS "rewards(address)(uint256)" $DEPLOYED_CONTRACT --rpc-url $RPC_URL\n')),(0,r.kt)("p",null,"And finally, all balance (including rewards) can we removed from the deployed smart contract by calling the ",(0,r.kt)("inlineCode",{parentName:"p"},"withdraw")," function:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'cast send $DEPLOYED_CONTRACT "withdraw()()" --rpc-url $RPC_URL --legacy --private-key $PRIVATE_KEY\n')))}u.isMDXComponent=!0}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.