1"use strict";(self.webpackChunkpendle_documentation=self.webpackChunkpendle_documentation||[]).push([[8069],{7156:(e,r,n)=>{n.r(r),n.d(r,{assets:()=>l,contentTitle:()=>s,default:()=>p,frontMatter:()=>t,metadata:()=>a,toc:()=>d});const a=JSON.parse('{"id":"Contracts/Oracle/LinearDiscountOracle/LinearDiscountOracleWrapper","title":"Linear Discount Oracle Wrapper","description":"Sources:","source":"@site/docs/pendle-dev-docs/Contracts/Oracle/LinearDiscountOracle/LinearDiscountOracleWrapper.md","sourceDirName":"Contracts/Oracle/LinearDiscountOracle","slug":"/Contracts/Oracle/LinearDiscountOracle/LinearDiscountOracleWrapper","permalink":"/pendle-v2-dev/Contracts/Oracle/LinearDiscountOracle/LinearDiscountOracleWrapper","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"hide_table_of_contents":true},"sidebar":"pendleDevSidebar","previous":{"title":"Chainlink Oracle","permalink":"/pendle-v2-dev/Contracts/Oracle/ChainlinkOracle"},"next":{"title":"LP Linear Discount Oracle","permalink":"/pendle-v2-dev/Contracts/Oracle/LinearDiscountOracle/LpLinearDiscountOracle"}}');var c=n(4848),i=n(8453);const t={hide_table_of_contents:!0},s="Linear Discount Oracle Wrapper",l={},d=[{value:"Overview",id:"overview",level:2},{value:"When to Use the Wrapper",id:"when-to-use-the-wrapper",level:2},{value:"<code>PendleLinearDiscountOracleWrapper</code>",id:"pendlelineardiscountoraclewrapper",level:2},{value:"<code>latestRoundData</code>",id:"latestrounddata",level:3},{value:"<code>decimals</code>",id:"decimals",level:3},{value:"<code>PendleSparkLinearDiscountOracleFactoryWrapper</code>",id:"pendlesparklineardiscountoraclefactorywrapper",level:2},{value:"<code>createWithPt</code>",id:"createwithpt",level:3},{value:"<code>createWithMarket</code>
1",id:"createwithmarket",level:3},{value:"<code>wrap</code>",id:"wrap",level:3},{value:"Further Reading",id:"further-reading",level:2}];function o(e){const r={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",hr:"hr",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,i.R)(),...e.components};return(0,c.jsxs)(c.Fragment,{children:[(0,c.jsx)(r.header,{children:(0,c.jsx)(r.h1,{id:"linear-discount-oracle-wrapper",children:"Linear Discount Oracle Wrapper"})}),"\n",(0,c.jsx)(r.p,{children:(0,c.jsx)(r.strong,{children:"Sources:"})}),"\n",(0,c.jsxs)(r.ul,{children:["\n",(0,c.jsx)(r.li,{children:(0,c.jsx)(r.a,{href:"https://github.com/pendle-finance/pendle-core-v2-public/blob/main/contracts/oracles/StatelessWrapper/PendleLinearDiscountOracleWrapper.sol",children:(0,c.jsx)(r.code,{children:"PendleLinearDiscountOracleWrapper.sol"})})}),"\n",(0,c.jsx)(r.li,{children:(0,c.jsx)(r.a,{href:"https://github.com/pendle-finance/pendle-core-v2-public/blob/main/contracts/oracles/StatelessWrapper/PendleSparkLinearDiscountOracleFactoryWrapper.sol",children:(0,c.jsx)(r.code,{children:"PendleSparkLinearDiscountOracleFactoryWrapper.sol"})})}),"\n"]}),"\n",(0,c.jsx)(r.h2,{id:"overview",children:"Overview"}),"\n",(0,c.jsxs)(r.p,{children:[(0,c.jsx)(r.code,{children:"PendleLinearDiscountOracleWrapper"}
1)," is a thin stateless wrapper that fixes the one incompatibility between Pendle's deterministic oracles and Chainlink-consuming protocols: the ",(0,c.jsx)(r.code,{children:"updatedAt"})," field."]}),"\n",(0,c.jsxs)(r.p,{children:["Pendle's deterministic oracles (",(0,c.jsx)(r.code,{children:"PendleSparkLinearDiscountOracle"}),", ",(0,c.jsx)(r.code,{children:"PendleLpLinearDiscountOracle"}),") return ",(0,c.jsx)(r.code,{children:"updatedAt = 0"})," in ",(0,c.jsx)(r.code,{children:"latestRoundData()"})," because their prices are computed entirely from ",(0,c.jsx)(r.code,{children:"block.timestamp"}),' and immutable constructor parameters \u2014 they are never "stale" by design. However, most lending protocols enforce a staleness check of the form ',(0,c.jsx)(r.code,{children:"block.timestamp - updatedAt < threshold"}),", which will reject any feed that returns ",(0,c.jsx)(r.code,{children:"updatedAt = 0"}),"."]}),"\n",(0,c.jsxs)(r.p,{children:["The wrapper solves this by forwarding all fields from the inner oracle unchanged, ",(0,c.jsxs)(r.strong,{children:["except ",(0,c.jsx)(r.code,{children:"updatedAt"})]}),", which it overwrites with ",(0,c.jsx)(r.code,{children:"block.timestamp"}),"."]}),"\n",(0,c.jsx)(r.hr,{}),"\n",(0,c.jsx)(r.h2,{id:"when-to-use-the-wrapper",children:"When to Use the Wrapper"}),"\n",(0,c.jsx)(r.p,{children:(0,c.jsxs)(r.strong,{children:["Does your protocol check ",(0,c.jsx)(r.code,{children:"updatedAt"})," staleness?"]})}),"\n",(0,c.jsxs)(r.ul,{children:["\n",(0,c.jsxs)(r.li,{children:[(0,c.jsx)(r.strong,{children:"No"})," \u2014 Deploy ",(0,c.jsx)(r.code,{children:"PendleSparkLinearDiscountOracle"})," directly via ",(0,c.jsx)(r.code,{children:"PendleSparkLinearDiscountOracleFactory"}),"."]}),"\n",(0,c.jsxs)(r.li,{children:[(0,c.jsx)(r.strong,{children:"Yes"})," (most lending protocols) \u2014 Deploy via ",(0,c.jsx)(r.code,{children:"PendleSparkLinearDiscountOracleFactoryWrapper"}),". The wrapper always returns ",(0,c.jsx)(r.code,{children:"updatedAt = block.timestamp"}),", satisfying any staleness threshold."]}),"\n",(0,c.jsxs)(r.li,{children:[(0,c.jsx)(r.strong,{children:"You already have a bare oracle"})," \u2014 Call ",(0,c.jsx)(r.code,{children:"PendleSparkLinearDiscountOracleFactoryWrapper.wrap(existingOracle)"})," to add the wrapper without redeploying."]}),"\n"]}),"\n",(0,c.jsxs)(r.admonition,{title:"LP oracle has no factory wrapper",type:"note",children:[(0,c.jsxs)(r.p,{children:[(0,c.jsx)(r.code,{children:"PendleLpLinearDiscountOracle"})," does not have a dedicated factory wrapper contract. To apply the wrapper to an LP oracle, deploy ",(0,c.jsx)(r.code,{children:"PendleLpLinearDiscountOracle"})," via ",(0,c.jsx)(r.code,{children:"PendleLpLinearDiscountOracleFactory"}),", then wrap the deployed address manually:"]}),(0,c.jsx)(r.pre,{children:(0,c.jsx)(r.code,{className:"language-solidity",children:"address lpOracle = lpFactory.create(market, baseLpDiscountPerYear, lpMaturedPrice);\naddress lpWrapped = address(new PendleLinearDiscountOracleWrapper(lpOracle));\n"})})]}),"\n",(0,c.jsx)(r.hr,{}),"\n",(0,c.jsx)(r.h2,{id:"pendlelineardiscountoraclewrapper",children:(0,c.jsx)(r.code,{children:"PendleLinearDiscountOracleWrapper"})}),"\n",(0,c.jsxs)(r.p,{children:["A minimal proxy that wraps ",(0,c.jsx)(r.strong,{children:"any"})," ",(0,c.jsx)(r.code,{children:"IPChainlinkOracleEssential"}),"-compatible oracle and replaces its ",(0,c.jsx)(r.code,{children:"updatedAt"})," with ",(0,c.jsx)(r.code,{children:"block.timestamp"}),"."]}),"\n",(0,c.jsx)(r.pre,{children:(0,c.jsx)(r.code,{className:"language-solidity",children:"contract PendleLinearDiscountOracleWrapper is IPChainlinkOracleEssential {\n IPChainlinkOracleEssential public immutable innerOracle;\n\n constructor(address _innerOracle);\n}\n"})}),"\n",(0,c.jsx)(r.h3,{id:"latestrounddata",children:(0,c.jsx)(r.code,{children:"latestRoundData"})}),"\n",(0,c.jsx)(r.pre,{children:(0,c.jsx)(r.code,{className:"language-solidity",children:"function latestRoundData()\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt, // overwritten to block.timestamp\n uint80 answeredInRound\n );\n"})}),"\n",(0,c.jsxs)(r.p,{children:["All fields are forwarded from ",(0,c.jsx)(r.code,{children:"innerOracle.latestRoundData()"}),", except ",(0,c.jsx)(r.code,{children:"updatedAt"})," which is replaced with ",(0,c.jsx)(r.code,{children:"block.timestamp"}),". The ",(0,c.jsx)(r.code,{children:"answer"})," (the discount factor or LP price) is not modified in any way."]}),"\n",(0,c.jsx)(r.h3,{id:"decimals",children:(0,c.jsx)(r.code,{children:"decimals"})}),"\n",(0,c.jsx)(r.pre,{children:(0,c.jsx)(r.code,{className:"language-solidity",children:"function decimals() external view returns (uint8);\n"})}),"\n",(0,c.jsxs)(r.p,{children:["Delegates directly to ",(0,c.jsx)(r.code,{children:"innerOracle.decimals()"}),". Returns ",(0,c.jsx)(r.code,{children:"18"})," for all Pendle deterministic oracles."]}),"\n",(0,c.jsx)(r.hr,{}),"\n",(0,c.jsx)(r.h2,{id:"pendlesparklineardiscountoraclefactorywrapper",children:(0,c.jsx)(r.code,{children:"PendleSparkLinearDiscountOracleFactoryWrapper"})}),"\n",(0,c.jsxs)(r.p,{children:["A convenience contract that combines ",(0,c.jsx)(r.code,{children:"PendleSparkLinearDiscountOracleFactory"})," and ",(0,c.jsx)(r.code,{children:"PendleLinearDiscountOracleWrapper"})," into a single deployment step. Calling ",(0,c.jsx)(r.code,{children:"createWithPt"})," or ",(0,c.jsx)(r.code,{children:"createWithMarket"})," on this contract:"]}),"\n",(0,c.jsxs)(r.ol,{children:["\n",(0,c.jsxs)(r.li,{children:["Deploys a ",(0,c.jsx)(r.code,{children:"PendleSparkLinearDiscountOracle"})," (via the inner factory)"]}),"\n",(0,c.jsxs)(r.li,{children:["Immediately wraps it in ",(0,c.jsx)(r.code,{children:"PendleLinearDiscountOracleWrapper"})]}),"\n",(0,c.jsxs)(r.li,{children:["Returns the ",(0,c.jsx)(r.strong,{children:"wrapper address"})," \u2014 the address your protocol should point at"]}),"\n"]}),"\n",(0,c.jsxs)(r.p,{children:["Emits ",(0,c.jsx)(r.code,{children:"WrapperOracleCreated(address indexed innerOracle, address indexed wrapper)"})," on each deployment."]}),"\n",(0,c.jsx)(r.pre,{children:(0,c.jsx)(r.code,{className:"language-solidity",children:"contract PendleSparkLinearDiscountOracleFactoryWrapper {\n PendleSparkLinearDiscountOracleFactory public immutable innerFactory;\n\n constructor(address _innerFactory);\n}\n"})}),"\n",(0,c.jsx)(r.h3,{id:"createwithpt",children:(0,c.jsx)(r.code,{children:"createWithPt"})}),"\n",(0,c.jsx)(r.pre,{children:(0,c.jsx)(r.code,{className:"language-solidity",children:"function createWithPt(\n address pt,\n uint256 baseDiscountPerYear\n) external returns (address
1wrapper);\n"})}),"\n",(0,c.jsxs)(r.p,{children:["Deploys a ",(0,c.jsx)(r.code,{children:"PendleSparkLinearDiscountOracle"})," for the given PT address, then wraps it. Returns the wrapper address."]}),"\n",(0,c.jsx)(r.h3,{id:"createwithmarket",children:(0,c.jsx)(r.code,{children:"createWithMarket"})}),"\n",(0,c.jsx)(r.pre,{children:(0,c.jsx)(r.code,{className:"language-solidity",children:"function createWithMarket(\n address market,\n uint256 baseDiscountPerYear\n) external returns (address wrapper);\n"})}),"\n",(0,c.jsxs)(r.p,{children:["Derives the PT address from ",(0,c.jsx)(r.code,{children:"IPMarket(market).readTokens()"}),", then proceeds identically to ",(0,c.jsx)(r.code,{children:"createWithPt"}),"."]}),"\n",(0,c.jsx)(r.h3,{id:"wrap",children:(0,c.jsx)(r.code,{children:"wrap"})}),"\n",(0,c.jsx)(r.pre,{children:(0,c.jsx)(r.code,{className:"language-solidity",children:"function wrap(address innerOracle) public returns (address wrapper);\n"})}),"\n",(0,c.jsxs)(r.p,{children:["Wraps an already-deployed ",(0,c.jsx)(r.code,{children:"IPChainlinkOracleEssential"}),"-compatible oracle in a new ",(0,c.jsx)(r.code,{children:"PendleLinearDiscountOracleWrapper"}),". Useful if you deployed an oracle via ",(0,c.jsx)(r.code,{children:"PendleSparkLinearDiscountOracleFactory"})," directly and need to add the wrapper afterwards."]}),"\n",(0,c.jsxs)(r.p,{children:[(0,c.jsx)(r.strong,{children:"Deployment addresses:"})," Find the factory wrapper address for each chain under ",(0,c.jsx)(r.code,{children:'"sparkLinearDiscountOracleFactoryWrapper"'})," in the ",(0,c.jsx)(r.a,{href:"https://github.com/pendle-finance/pendle-core-v2-public/tree/main/deployments",children:"Deployments on GitHub"}),"."]}),"\n",(0,c.jsx)(r.hr,{}),"\n",(0,c.jsx)(r.h2,{id:"further-reading",children:"Further Reading"}),"\n",(0,c.jsxs)(r.ul,{children:["\n",(0,c.jsxs)(r.li,{children:[(0,c.jsx)(r.a,{href:"/pendle-v2-dev/Contracts/Oracle/LinearDiscountOracle/SparkLinearDiscountOracle",children:"Spark Linear Discount Oracle"})," \u2014 full contract reference for the PT oracle"]}),"\n",(0,c.jsxs)(r.li,{children:[(0,c.jsx)(r.a,{href:"/pendle-v2-dev/Contracts/Oracle/LinearDiscountOracle/LpLinearDiscountOracle",children:"LP Linear Discount Oracle"})," \u2014 full contract reference for the LP oracle"]}),"\n",(0,c.jsxs)(r.li,{children:[(0,c.jsx)(r.a,{href:"/pendle-v2-dev/Oracles/DeterministicOracles/ChoosingLinearDiscountParams",children:"Choosing Linear Discount Parameters"})," \u2014 guidance on setting ",(0,c.jsx)(r.code,{children:"baseDiscountPerYear"})]}),"\n"]})]})}function p(e={}){const{wrapper:r}={...(0,i.R)(),...e.components};return r?(0,c.jsx)(r,{...e,children:(0,c.jsx)(o,{...e})}):o(e)}},8453:(e,r,n)=>{n.d(r,{R:()=>t,x:()=>s});var a=n(6540);const c={},i=a.createContext(c);function t(e){const r=a.useContext(i);return a.useMemo(function(){return"function"==typeof e?e(r):{...r,...e}},[r,e])}function s(e){let r;return r=e.disableParentContext?"function"==typeof e.components?e.components(c):e.components||c:t(e.components),a.createElement(i.Provider,{value:r},e.children)}}}]);
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.