> ## Documentation Index
> Fetch the complete documentation index at: https://docs.compasslabs.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Build an earn product with Compass API

> Learn how to easily build an `Earn` product using Compass API!

export const GithubCodeBlock = ({typescript, python}) => {
  const [typescriptCode, setTypescriptCode] = useState("");
  const [pythonCode, setPythonCode] = useState("");
  function removeWhitespace(strings, x) {
    return strings.map(str => {
      let count = 0;
      let i = 0;
      while (i < str.length && count < x && str[i] === " ") {
        count++;
        i++;
      }
      return str.slice(i);
    });
  }
  function cropToSnippet({text, from, to}) {
    const lines = text.split("\n");
    let result = [];
    let isCollecting = false;
    for (const line of lines) {
      if (line.trim() === from) {
        isCollecting = true;
        continue;
      }
      if (line.trim() === to) {
        break;
      }
      if (isCollecting) {
        result.push(line);
      }
    }
    const whitespaceToDelete = result[0].length - result[0].trimStart().length;
    result = removeWhitespace(result, whitespaceToDelete);
    return result.join("\n");
  }
  function removeSnippetComments(code) {
    return code.replace(/(\/\/|#) SNIPPET (START|END) \d+(\n|$)/g, "");
  }
  useEffect(() => {
    if (!typescript) return;
    fetch(typescript.url).then(response => response.text()).then(text => {
      let code = text;
      if (typescript.snippetNumber) {
        code = cropToSnippet({
          text,
          from: `// SNIPPET START ${typescript.snippetNumber}`,
          to: `// SNIPPET END ${typescript.snippetNumber}`
        });
      } else {
        code = removeSnippetComments(code);
      }
      setTypescriptCode(code);
    }).catch(error => {
      console.error("Error loading file:", error);
    });
  }, [typescript]);
  useEffect(() => {
    if (!python) return;
    fetch(python.url).then(response => response.text()).then(text => {
      let code = text;
      if (python.snippetNumber) {
        code = cropToSnippet({
          text,
          from: `# SNIPPET START ${python.snippetNumber}`,
          to: `# SNIPPET END ${python.snippetNumber}`
        });
      } else {
        code = removeSnippetComments(code);
      }
      setPythonCode(code);
    }).catch(error => {
      console.error("Error loading file:", error);
    });
  }, [python]);
  if (!python && !typescript) return null;
  if (!python) {
    return <CodeBlock language="typescript" filename={typescript.name} expandable={typescript?.numOfLinesExpandable ? "true" : null} lines="true" icon="https://upload.wikimedia.org/wikipedia/commons/f/f5/Typescript.svg" code={typescriptCode} children={typescriptCode || !typescript?.numOfLinesExpandable || <div style={{
      whiteSpace: "pre-wrap"
    }}>
        {Array(typescript?.numOfLinesExpandable).fill("-").map(x => x).join("\n")}
      </div>} />;
  }
  if (!typescript) {
    return <CodeBlock language="python" filename={python.name} expandable={python?.numOfLinesExpandable ? "true" : null} lines="true" icon="https://upload.wikimedia.org/wikipedia/commons/c/c3/Python-logo-notext.svg" code={pythonCode} children={pythonCode || !python?.numOfLinesExpandable || <div style={{
      whiteSpace: "pre-wrap"
    }}>
        {Array(python?.numOfLinesExpandable).fill("-").map(x => x).join("\n")}
      </div>} />;
  }
  return <CodeGroup>
  <CodeBlock language="typescript" filename={typescript.name} expandable={typescript?.numOfLinesExpandable ? "true" : null} lines="true" icon="https://upload.wikimedia.org/wikipedia/commons/f/f5/Typescript.svg" code={typescriptCode} children={typescriptCode || !typescript?.numOfLinesExpandable || <div style={{
    whiteSpace: "pre-wrap"
  }}>
          {Array(typescript?.numOfLinesExpandable).fill("-").map(x => x).join("\n")}
        </div>} />
  <CodeBlock language="python" filename={python.name} expandable={python?.numOfLinesExpandable ? "true" : null} lines="true" icon="https://upload.wikimedia.org/wikipedia/commons/c/c3/Python-logo-notext.svg" code={pythonCode} children={pythonCode || !python?.numOfLinesExpandable || <div style={{
    whiteSpace: "pre-wrap"
  }}>
          {Array(python?.numOfLinesExpandable).fill("-").map(x => x).join("\n")}
        </div>} />
</CodeGroup>;
};

Check out the deployed demo [here](https://wallet-demo-sand.vercel.app/).

<div className="flex justify-center">
  <img src="https://mintcdn.com/compasslabs-f07467e0/owYz5QvNE867Wc23/images/wallet_demo2.gif?s=9b7b5486fa576d1fbf6df0e0f1f56aa0" alt="wallet_demo" width="200" data-path="images/wallet_demo2.gif" />
</div>

Compass API is well suited for adding earn features to existing custody solutions.

In the following example, we are adding an `Earn` feature to a hypothetical wallet provider.

You can find the code on [Github](https://github.com/CompassLabs/api_usecases/tree/main/wallet-earn).

## This single flow demonstrates

1. Swapping between stablecoins within the Product Account
2. Depositing into yield-generating vaults
3. Gas sponsorship so user never needs ETH
4. Embedded fees so you monetize the transaction
5. Position monitoring for dashboards and risk management
6. All bundled atomically in one user signature

## Key takeaways

* For the end user, everything is fully abstracted away. They just sign on transactions, as always.
* The end user can deposit and withdraw, without requiring gas tokens
* We don't require the user to sign token allowances
* The wallet provider earns a small fee on every vault deposit.
* Everything has been implemented fully using the Compass typescript SDK

## Step by step

### Package imports

Compass API can be used directly, or used via typescript SDK.

<GithubCodeBlock
  typescript={{
name: "deposit.ts",
url: "https://raw.githubusercontent.com/CompassLabs/api_usecases/refs/heads/main/wallet-earn/app/api/deposit/route.ts",
snippetNumber: 1,
}}
/>

### Call EarnManage Endpoint

Your main point of entry for managing positions is the [Manage earn position](https://docs.compasslabs.ai/v2/api-reference/earn/manage-earn-position) endpoint.
Compass returns an unsigned intent-payload.

<GithubCodeBlock
  typescript={{
name: "deposit.ts",
url: "https://raw.githubusercontent.com/CompassLabs/api_usecases/refs/heads/main/wallet-earn/app/api/deposit/route.ts",
snippetNumber: 2,
}}
/>

{/* The users simply signs the payload with standard cryptographic signatures. */}

{/*   typescript={{ */}

{/* url: "https://raw.githubusercontent.com/CompassLabs/api_usecases/refs/heads/main/wallet-earn/app/api/deposit/route.ts", */}

{/* }} */}

### Create 712 signature

Users simply sign the payload with standard cryptographic signatures.

<GithubCodeBlock
  typescript={{
name: "deposit.ts",
url: "https://raw.githubusercontent.com/CompassLabs/api_usecases/refs/heads/main/wallet-earn/app/api/deposit/route.ts",
snippetNumber: 3,
}}
/>

### Call to request gas sponsorship

The gas sponsor (in this case the wallet), calls the gas sponsorship endpoint.
This is optional, the end user can also submit transactions themselves and pay for gas.

<GithubCodeBlock
  typescript={{
name: "deposit.ts",
url: "https://raw.githubusercontent.com/CompassLabs/api_usecases/refs/heads/main/wallet-earn/app/api/deposit/route.ts",
snippetNumber: 4,
}}
/>

### Gas Sponsor signs and submits transaction.

The gas sponsor signs and submits the transaction.

<GithubCodeBlock
  typescript={{
name: "deposit.ts",
url: "https://raw.githubusercontent.com/CompassLabs/api_usecases/refs/heads/main/wallet-earn/app/api/deposit/route.ts",
snippetNumber: 5,
}}
/>
