1"use strict";(self.webpackChunknifty_docs=self.webpackChunknifty_docs||[]).push([[6656],{9307:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>c,default:()=>p,frontMatter:()=>i,metadata:()=>l,toc:()=>u});var s=n(6070),r=n(5710),a=n(6911),o=n(2405);const i={sidebar_label:"Proxy",sidebar_position:10},c="Proxy",l={id:"asset/extensions/proxy",title:"Proxy",description:'In addition to extensions, Nifty Asset implements the \u2398Proxy Pattern to allow developers extend the program behaviour. The Proxy extension is used to identify accounts that are "proxied" — these accounts will be required to be a signer on instructions that manipulate them. Since only the proxy program can add the signature for the account — the asset address is a PDA of the proxy program — it guarantees that it is always called on every instruction and it is allowed to perform custom logic.',source:"@site/docs/asset/extensions/proxy.md",sourceDirName:"asset/extensions",slug:"/asset/extensions/proxy",permalink:"/docs/asset/extensions/proxy",draft:!1,unlisted:!1,tags:[],version:"current",sidebarPosition:10,frontMatter:{sidebar_label:"Proxy",sidebar_position:10},sidebar:"docsSidebar",previous:{title:"Properties",permalink:"/docs/asset/extensions/properties"},next:{title:"Royalties",permalink:"/docs/asset/extensions/royalties"}},d={},u=[{value:"Creating a "Proxied" Asset",id:"creating-a-proxied-asset",level:2},{value:"Fetching a "Proxied" Asset",id:"fetching-a-proxied-asset",level:2},{value:"Using a "Proxied" Asset",id:"using-a-proxied-asset",level:2}];function h(e){const t={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",li:"li",ol:"ol",p:"p",pre:"pre",...(0,r.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(t.h1,{id:"proxy",children:"Proxy"}),"\n",(0,s.jsxs)(t.p,{children:["In addition to extensions, Nifty Asset implements the ",(0,s.jsx)(t.a,{href:"/blog/proxy-pattern",children:"\u2398Proxy Pattern"})," to allow developers extend the program behaviour. The ",(0,s.jsx)(t.code,{children:"Proxy"}),' extension is used to identify accounts that are "proxied" \u2014 these accounts will be required to be a signer on instructions that manipulate them. Since only the proxy program can add the signature for the account \u2014 the asset address is a PDA of the proxy program \u2014 it guarantees that it is always called on every instruction and it is allowed to perform custom logic.']}),"\n",(0,s.jsxs)(t.p,{children:["The extension stores the information for clients to determine the address of the proxy program; it also includes the ",(0,s.jsx)(t.code,{children:"seeds"})," and ",(0,s.jsx)(t.code,{children:"bump"})," information for a proxy program to add a signature for the account."]}),"\n",(0,s.jsxs)("table",{class:"account-layout-table",children:[(0,s.jsx)("thead",{children:(0,s.jsxs)("tr",{children:[(0,s.jsx)("th",{children:(0,s.jsx)("i",{children:"Field"})}),(0,s.jsx)("th",{children:(0,s.jsx)("i",{children:"Description"})})]})}),(0,s.jsxs)("tbody",{children:[(0,s.jsxs)("tr",{children:[(0,s.jsx)("td",{children:(0,s.jsx)("code",{children:"program"})}),(0,s.jsx)("td",{children:"Proxy program address."})]}),(0,s.jsxs)("tr",{children:[(0,s.jsx)("td",{children:(0,s.jsx)("code",{children:"seeds"})}),(0,s.jsx)("td",{children:"Seeds for the PDA derivation."})]}),(0,s.jsxs)("tr",{children:[(0,s.jsx)("td",{children:(0,s.jsx)("code",{children:"bump"})}),(0,s.jsx)("td",{children:"Bump for the PDA derivation."})]}),(0,s.jsxs)("tr",{children:[(0,s.jsx)("td",{children:(0,s.jsx)("code",{children:"authority"})}),(0,s.jsxs)("td",{children:["(optional) Authority of the proxy extension. This can be used to specify an address to act as the authority of the asset since in most cases the ",(0,s.jsx)(t.code,{children:"authority"})," on the asset is set to be itself."]})]})]})]}),"\n",(0,s.jsx)(t.h2,{id:"creating-a-proxied-asset",children:'Creating a "Proxied" Asset'}),"\n",(0,s.jsx)(t.p,{children:'A "proxied" asset must be created through a program, since it must be a PDA and the program needs to sign the account creation. On the program, the following steps must be completed:'}),"\n",(0,s.jsxs)(t.ol,{children:["\n",(0,s.jsxs)(t.li,{children:["Create the ",(0,s.jsx)(t.code,{children:"Proxy"})," extension for the asset."]}),"\n",(0,s.jsxs)(t.li,{children:["Set the standard of the asset to ",(0,s.jsx)(t.code,{children:"Standard::Proxied"}),"."]}),"\n",(0,s.jsx)(t.li,{children:"Sign the instruction to create the asset."}),"\n"]}),"\n","\n",(0,s.jsx)(a.A,{children:(0,s.jsx)(o.A,{value:"rust on-chain",label:"Rust (on-chain)",children:(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-rust",children:'use nifty_asset_interface::{\n extensions::ProxyBuilder,\n instructions::CreateCpiBuilder,\n types::ExtensionInput,\n ExtensionType, Standard,\n};\nuse solana_program::pubkey::Pubkey;\n\n// validation: derived_key == *ctx.accounts.asset.key\nlet (derived_key, bump) = Pubkey::find_program_address(\n &[ctx.accounts.stub.key.as_ref()],\n program_id\n);\nlet signer = [ctx.accounts.stub.key.as_ref(), &[bump]];\n\n// proxy extension data\nlet data = ProxyBuilder::with_capacity(100)\n .set(\n program_id,\n &ctx.accounts.stub.key.to_bytes(),\n bump,\n // "proxy" authority\n Some(ctx.accounts.authority.key),\n )\n .data();\n\n// creates the asset\nCreateCpiBuilder::new(ctx.accounts.nifty_asset_program)\n .asset(ctx.accounts.asset)\n // keeps the authority so we can update the asset\n .authority(ctx.accounts.asset, true)\n .owner(ctx.accounts.owner)\n .payer(ctx.accounts.payer)\n .system_program(ctx.accounts.system_program)\n .name(metadata.name)\n .standard(Standard::Proxied)\n .extensions(vec![ExtensionInput {\n extension_type: ExtensionType::Proxy,\n length: data.len() as u32,\n data: Some(data),\n }])\n .invoke_signed(&[&signer])\n'})})})}),"\n",(0,s.jsx)(t.p,{children:'Programs that create "proxied" assets are dubbed "Proxy" programs. They will always be involved in any transaction that manipulate the "proxied" asset, since they need to (at least) sign the instruction on behalf of the asset. As a result, they can perform any custom action, providing an easy way to add custom behaviour to Nifty Asset.'}),"\n",(0,s.jsxs)(t.p,{children:["The ",(0,s.jsx)("u",{children:(0,s.jsx)(t.a,{href:"https://crates.io/crates/nifty-asset-interface",children:"\u2398Nifty Asset Interface"})}),' crate provides the program interface and helpers to support the development of "Proxy" programs. The Nifty Asset repository contains an ',(0,s.jsx)("u",{children:(0,s.jsx)(t.a,{href:"https://github.com/nifty-oss/asset/tree/main/programs/proxy",children:'\u2398example "Proxy" program'})})," showing how to use this crate to add custom behaviour to ",(0,s.jsx)(t.code,{children:"transfer"})," and ",(0,s.jsx)(t.code,{children:"update"})," instructions."]}),"\n",(0,s.jsx)(t.admonition,{type:"info",children:(0,s.jsxs)(t.p,{children:["The ",(0,s.jsx)(t.code,{children:"Proxy"})," extension must be specified at the point of creation of the asset. It is not possible to add or remove the extension after its creation. It is possible, however, to update its ",(0,s.jsx)(t.code,{children:"authority"}),"."]})}),"\n",(0,s.jsx)(t.h2,{id:"fetching-a-proxied-asset",children:'Fetching a "Proxied" Asset'}),"\n",(0,s.jsx)(t.p,{children:"Given an asset account, it is possible to retrieve the proxy information of an asset. Note that not all assets might have the extension, therefore it is necessary to assert if the extension was found."}),"\n",(0,s.jsxs)(a.A,{children:[(0,s.jsx)(o.A,{value:"javascript",label:"JavaScript",default:!0,children:(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-js",children:'import {\n ExtensionType,\n fetchAsset,\n getExtension\n} from \'@nifty-oss/asset\';\n\nconst asset = await fetchAsset(umi, address);\nconst proxy = getExtension(asset, ExtensionType.Proxy);
1\n\nif (proxy) {\n console.log("Proxy program: " + proxy.program);\n console.log("Authority: " + proxy.authority);\n}\n'})})}),(0,s.jsx)(o.A,{value:"rust",label:"Rust",children:(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-rust",children:'use nifty_asset::{\n extensions::Proxy,\n state::Asset,\n};\n\nlet account = get_account(address)\n .await\n .unwrap();\nlet account_data = account.data.as_ref();\n\nif let Some(proxy) = Asset::get::<Proxy>(account_data) {\n println!("proxy program: {:?}", proxy.program);\n println!("authority: {:?}", proxy.authority);\n}\n'})})}),(0,s.jsx)(o.A,{value:"rust on-chain",label:"Rust (on-chain)",children:(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-rust",children:'use nifty_asset::{\n extensions::Proxy,\n state::Asset,\n};\n\nlet data = (*ctx.accounts.asset.data).borrow();\n\nif let Some(proxy) = Asset::get::<Proxy>(&data) {\n msg!("proxy program: {:?}", proxy.program);\n msg!("authority: {:?}", proxy.authority);\n}\n'})})})]}),"\n",(0,s.jsx)(t.h2,{id:"using-a-proxied-asset",children:'Using a "Proxied" Asset'}),"\n",(0,s.jsx)(t.p,{children:'"Proxied" assets are a special category of assets, since they asset account must be a signer on every transaction that manipulates the asset. Since the asset is created from a PDA of the "Proxy" program, the proxy program must be the one to add the signature for the asset account. Therefore, it is necessary to "proxy" instructions through it \u2014 that is the reason why they are called "Proxy" programs.'}),"\n",(0,s.jsxs)(t.p,{children:["Since all the information needed for this is stored on the ",(0,s.jsx)(t.code,{children:"Proxy"}),' extension, it is easy to determine whether we are handling a "proxied" asset and its proxy program.']}),"\n",(0,s.jsx)(a.A,{children:(0,s.jsx)(o.A,{value:"javascript",label:"JavaScript",default:!0,children:(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-js",children:"import {\n ExtensionType,\n fetchAsset,\n getExtension,\n transfer,\n} from '@nifty-oss/asset';\n\nconst asset = await fetchAsset(umi, address);\nconst proxy = getExtension(asset, ExtensionType.Proxy);\n\nawait transfer (umi, {\n asset: address,\n signer: owner,\n recipient,\n proxy: proxy?.program,\n}).sendAndConfirm(umi);\n"})})})})]})}function p(e={}){const{wrapper:t}={...(0,r.R)(),...e.components};return t?(0,s.jsx)(t,{...e,children:(0,s.jsx)(h,{...e})}):h(e)}},2405:(e,t,n)=>{n.d(t,{A:()=>o});n(758);var s=n(3526);const r={tabItem:"tabItem_r0H8"};var a=n(6070);function o(e){let{children:t,hidden:n,className:o}=e;return(0,a.jsx)("div",{role:"tabpanel",className:(0,s.A)(r.tabItem,o),hidden:n,children:t})}},6911:(e,t,n)=>{n.d(t,{A:()=>w});var s=n(758),r=n(3526),a=n(192),o=n(5557),i=n(3777),c=n(1997),l=n(7650),d=n(1607);function u(e){return s.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,s.isValidElement)(e)&&function(e){const{props:t}=e;return!!t&&"object"==typeof t&&"value"in t}(e))return e;throw new Error(`Docusaurus error: Bad <Tabs> child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function h(e){const{values:t,children:n}=e;return(0,s.useMemo)((()=>{const e=t??function(e){return u(e).map((e=>{let{props:{value:t,label:n,attributes:s,default:r}}=e;return{value:t,label:n,attributes:s,default:r}}))}(n);return function(e){const t=(0,l.X)(e,((e,t)=>e.value===t.value));if(t.length>0)throw new Error(`Docusaurus error: Duplicate values "${t.map((e=>e.value)).join(", ")}" found in <Tabs>. Every value needs to be unique.`)}(e),e}),[t,n])}function p(e){let{value:t,tabValues:n}=e;return n.some((e=>e.value===t))}function x(e){let{queryString:t=!1,groupId:n}=e;const r=(0,o.W6)(),a=function(e){let{queryString:t=!1,groupId:n}=e;if("string"==typeof t)return t;if(!1===t)return null;if(!0===t&&!n)throw new Error('Docusaurus error: The <Tabs> component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return n??null}({queryString:t,groupId:n});return[(0,c.aZ)(a),(0,s.useCallback)((e=>{if(!a)return;const t=new URLSearchParams(r.location.search);t.set(a,e),r.replace({...r.location,search:t.toString()})}),[a,r])]}function m(e){const{defaultValue:t,queryString:n=!1,groupId:r}
1=e,a=h(e),[o,c]=(0,s.useState)((()=>function(e){let{defaultValue:t,tabValues:n}=e;if(0===n.length)throw new Error("Docusaurus error: the <Tabs> component requires at least one <TabItem> children component");if(t){if(!p({value:t,tabValues:n}))throw new Error(`Docusaurus error: The <Tabs> has a defaultValue "${t}" but none of its children has the corresponding value. Available values are: ${n.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return t}const s=n.find((e=>e.default))??n[0];if(!s)throw new Error("Unexpected error: 0 tabValues");return s.value}({defaultValue:t,tabValues:a}))),[l,u]=x({queryString:n,groupId:r}),[m,y]=function(e){let{groupId:t}=e;const n=function(e){return e?`docusaurus.tab.${e}`:null}(t),[r,a]=(0,d.Dv)(n);return[r,(0,s.useCallback)((e=>{n&&a.set(e)}),[n,a])]}({groupId:r}),f=(()=>{const e=l??m;return p({value:e,tabValues:a})?e:null})();(0,i.A)((()=>{f&&c(f)}),[f]);return{selectedValue:o,selectValue:(0,s.useCallback)((e=>{if(!p({value:e,tabValues:a}))throw new Error(`Can't select invalid tab value=${e}`);c(e),u(e),y(e)}),[u,y,a]),tabValues:a}}var y=n(6883);const f={tabList:"tabList_m2iM",tabItem:"tabItem_yoA2"};var g=n(6070);function b(e){let{className:t,block:n,selectedValue:s,selectValue:o,tabValues:i}=e;const c=[],{blockElementScrollPositionUntilNextRender:l}=(0,a.a_)(),d=e=>{const t=e.currentTarget,n=c.indexOf(t),r=i[n].value;r!==s&&(l(t),o(r))},u=e=>{let t=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const n=c.indexOf(e.currentTarget)+1;t=c[n]??c[0];break}case"ArrowLeft":{const n=c.indexOf(e.currentTarget)-1;t=c[n]??c[c.length-1];break}}t?.focus()};return(0,g.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,r.A)("tabs",{"tabs--block":n},t),children:i.map((e=>{let{value:t,label:n,attributes:a}=e;return(0,g.jsx)("li",{role:"tab",tabIndex:s===t?0:-1,"aria-selected":s===t,ref:e=>c.push(e),onKeyDown:u,onClick:d,...a,className:(0,r.A)("tabs__item",f.tabItem,a?.className,{"tabs__item--active":s===t}),children:n??t},t)}))})}function j(e){let{lazy:t,children:n,selectedValue:r}=e;const a=(Array.isArray(n)?n:[n]).filter(Boolean);if(t){const e=a.find((e=>e.props.value===r));return e?(0,s.cloneElement)(e,{className:"margin-top--md"}):null}return(0,g.jsx)("div",{className:"margin-top--md",children:a.map(((e,t)=>(0,s.cloneElement)(e,{key:t,hidden:e.props.value!==r})))})}function v(e){const t=m(e);return(0,g.jsxs)("div",{className:(0,r.A)("tabs-container",f.tabList),children:[(0,g.jsx)(b,{...t,...e}),(0,g.jsx)(j,{...t,...e})]})}function w(e){const t=(0,y.A)();return(0,g.jsx)(v,{...e,children:u(e.children)},String(t))}},5710:(e,t,n)=>{n.d(t,{R:()=>o,x:()=>i});var s=n(758);const r={},a=s.createContext(r);function o(e){const t=s.useContext(a);return s.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function i(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:o(e.components),s.createElement(a.Provider,{value:t},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.