Skip to content

Commit

Permalink
feat: add payment splitter
Browse files Browse the repository at this point in the history
  • Loading branch information
jinglescode committed May 8, 2024
1 parent 21cbf9b commit aeafce6
Show file tree
Hide file tree
Showing 11 changed files with 2,605 additions and 1,094 deletions.
2 changes: 1 addition & 1 deletion packages/demo/components/pages/contracts/giftcard/hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export default function Hero() {
</div>
</h2>
<p className="mb-8 font-light text-gray-500 sm:text-xl dark:text-gray-400">
Create a giftcard with native tokens
Create a giftcard with native tokens.
</p>
<p>
Giftcard contract allows users to create a transactions to lock assets
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ function Right() {
}

const tx = await contract.purchaseAsset(utxo);
console.log(1, 'tx', tx)
const signedTx = await wallet.signTx(tx, true);
console.log(2, 'signedTx', signedTx)
const txHash = await wallet.submitTx(signedTx);
console.log(4, txHash);
setResponse(txHash);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { MeshGiftCardContract } from '@meshsdk/contracts';
import { BlockfrostProvider, MeshTxBuilder } from '@meshsdk/core';

export function getContract(wallet) {
const blockchainProvider = new BlockfrostProvider(
process.env.NEXT_PUBLIC_BLOCKFROST_API_KEY_PREPROD!
);

const meshTxBuilder = new MeshTxBuilder({
fetcher: blockchainProvider,
submitter: blockchainProvider,
});

const contract = new MeshGiftCardContract({
mesh: meshTxBuilder,
fetcher: blockchainProvider,
wallet: wallet,
networkId: 0,
});

return contract;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import Codeblock from '../../../ui/codeblock';
import Card from '../../../ui/card';
import SectionTwoCol from '../../../common/sectionTwoCol';
import Button from '../../../ui/button';
import { CardanoWallet, useWallet } from '@meshsdk/react';
import { useState } from 'react';
import RunDemoResult from '../../../common/runDemoResult';
import { Asset } from '@meshsdk/core';
import useLocalStorage from '../../../../hooks/useLocalStorage';
import { getContract } from './common';

export default function GiftcardCreate() {
return (
<>
<SectionTwoCol
sidebarTo="createGiftCard"
header="Create Giftcard"
leftFn={Left()}
rightFn={Right()}
/>
</>
);
}

function Left() {
return (
<>
<p>
<code>createGiftCard()</code> create a gift card. The
function accepts the following parameters:
</p>
<ul>
<li>
<b>tokenName (string)</b> - name of the token
</li>
<li>
<b>giftValue (Asset[])</b> - a list of assets
</li>
</ul>
<p>
The function returns a transaction hash if the gift card is successfully
created.
</p>
<p>
The code snippet below demonstrates how to create a gift card with a
value of 20 ADA.
</p>
</>
);
}

function Right() {
const { connected, wallet } = useWallet();
const [loading, setLoading] = useState<boolean>(false);
const [response, setResponse] = useState<null | any>(null);
const [responseError, setResponseError] = useState<null | any>(null);
const [userLocalStorage, setUserlocalStorage] = useLocalStorage(
'mesh_giftcard_demo',
undefined
);

async function rundemo() {
setLoading(true);
setResponse(null);
setResponseError(null);

try {
const contract = getContract(wallet);

const tokenName = `Mesh_Gift_Card_${parseInt(
(Math.random() * 1000).toString()
)}`;
const giftValue: Asset[] = [
{
unit: 'lovelace',
quantity: '20000000',
},
];

const tx = await contract.createGiftCard(tokenName, giftValue);
const signedTx = await wallet.signTx(tx);
const txHash = await wallet.submitTx(signedTx);
setUserlocalStorage(txHash);
setResponse(txHash);
} catch (error) {
setResponseError(`${error}`);
}
setLoading(false);
}

let code = ``;
code += `const tokenName = 'Mesh Gift Card';\n`;
code += `const giftValue: Asset[] = [\n`;
code += ` {\n`;
code += ` unit: 'lovelace',\n`;
code += ` quantity: '20000000',\n`;
code += ` },\n`;
code += `];\n`;
code += `\n`;
code += `const tx = await contract.createGiftCard(tokenName, giftValue);\n`;
code += `const signedTx = await wallet.signTx(tx);\n`;
code += `const txHash = await wallet.submitTx(signedTx);\n`;

return (
<Card>
<p>This demo, we will create a giftcard containing 20 ADA.</p>
<Codeblock data={code} isJson={false} />
{connected ? (
<>
<Button
onClick={() => rundemo()}
style={
loading ? 'warning' : response !== null ? 'success' : 'light'
}
disabled={loading}
>
Create Giftcard
</Button>
<RunDemoResult response={response} />
</>
) : (
<CardanoWallet />
)}
<RunDemoResult response={responseError} label="Error" />
</Card>
);
}
71 changes: 71 additions & 0 deletions packages/demo/components/pages/contracts/payment-splitter/hero.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { ArrowsPointingOutIcon } from '@heroicons/react/24/solid';
import Codeblock from '../../../ui/codeblock';
import Link from 'next/link';

export default function Hero() {
let code = ``;
code += `import { BlockfrostProvider, MeshTxBuilder } from '@meshsdk/core';\n`;
code += `import { MeshGiftCardContract } from '@meshsdk/contracts';\n`;
code += `import { useWallet } from '@meshsdk/react';\n`;
code += `\n`;
code += `const { connected, wallet } = useWallet();\n`;
code += `\n`;
code += `const blockchainProvider = new BlockfrostProvider(APIKEY);\n`;
code += `\n`;
code += `const meshTxBuilder = new MeshTxBuilder({\n`;
code += ` fetcher: blockchainProvider,\n`;
code += ` submitter: blockchainProvider,\n`;
code += `});\n`;
code += `\n`;
code += `const contract = new MeshGiftCardContract({\n`;
code += ` mesh: meshTxBuilder,\n`;
code += ` fetcher: blockchainProvider,\n`;
code += ` wallet: wallet,\n`;
code += ` networkId: 0,\n`;
code += `});\n`;

return (
<>
<header className="mb-4 lg:mb-6">
<h2 className="mb-4 text-4xl tracking-tight font-extrabold text-gray-900 dark:text-white">
<div className="flex items-center">
<div className="p-2 mr-4">
<ArrowsPointingOutIcon className="w-16 h-16" />
</div>
<span>Payment Splitter</span>
</div>
</h2>
<p className="mb-8 font-light text-gray-500 sm:text-xl dark:text-gray-400">
Split contract payouts equally among all payees
</p>
<p>One liner description of the contract.</p>
<p>About this contract</p>
</header>
<div className="grid grid-cols-1 px-4 lg:grid-cols-3 lg:gap-4 pb-16">
<div className="col-span-2">
<p>
There are 2 actions (or endpoints) available to interact with this
smart contract:
</p>
<ul>
<li>create giftcard</li>
<li>redeem giftcard</li>
</ul>
<p>
To initialize the escrow, we need to initialize a{' '}
<Link href="/providers">provider</Link>, <code>MeshTxBuilder</code>{' '}
and <code>MeshPaymentSplitterContract</code>.
</p>
<Codeblock data={code} isJson={false} />
<p>
Both on-chain and off-chain codes are open-source and available on{' '}
<Link href="https://github.com/MeshJS/mesh/tree/main/packages/contracts/src/payment-splitter">
Mesh Github Repository
</Link>
.
</p>
</div>
</div>
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import CommonLayout from '../../../common/layout';
import GiftcardCreate from './createGiftCard';
import Hero from './hero';
import GiftcardRedeem from './redeemGiftCard';

export default function ContractsGiftcard() {
const sidebarItems = [
{ label: 'Create Giftcard', to: 'createGiftCard' },
{ label: 'Redeem Giftcard', to: 'redeemGiftCard' },
];

return (
<CommonLayout sidebarItems={sidebarItems}>
<Hero />
<Main />
</CommonLayout>
);
}

function Main() {
return (
<>
<GiftcardCreate />
<GiftcardRedeem />
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import Codeblock from '../../../ui/codeblock';
import Card from '../../../ui/card';
import SectionTwoCol from '../../../common/sectionTwoCol';
import Button from '../../../ui/button';
import { CardanoWallet, useWallet } from '@meshsdk/react';
import { useState } from 'react';
import RunDemoResult from '../../../common/runDemoResult';
import useLocalStorage from '../../../../hooks/useLocalStorage';
import { getContract } from './common';

export default function GiftcardRedeem() {
return (
<>
<SectionTwoCol
sidebarTo="redeemGiftCard"
header="Redeem Giftcard"
leftFn={Left()}
rightFn={Right()}
/>
</>
);
}

function Left() {
return (
<>
<p>
<code>redeemGiftCard()</code> redeem a gift
card. The function accepts the following parameters:
</p>
<ul>
<li>
<b>giftCardUtxo (UTxO)</b> - unspent transaction output in the script
</li>
</ul>
<p>
The function returns a transaction hash if the gift card is successfully
redeemed. It will burn the gift card and transfer the value to the
wallet signing this transaction.
</p>
<p>The code snippet below demonstrates how to redeem a gift card.</p>
</>
);
}

function Right() {
const { connected, wallet } = useWallet();
const [loading, setLoading] = useState<boolean>(false);
const [response, setResponse] = useState<null | any>(null);
const [responseError, setResponseError] = useState<null | any>(null);
const [userLocalStorage, setUserlocalStorage] = useLocalStorage(
'mesh_giftcard_demo',
undefined
);

async function rundemo() {
setLoading(true);
setResponse(null);
setResponseError(null);

try {
const contract = getContract(wallet);

const utxo = await contract.getUtxoByTxHash(userLocalStorage);

if (!utxo) {
setResponseError('Input utxo not found');
setLoading(false);
return;
}

const tx = await contract.redeemGiftCard(utxo);
const signedTx = await wallet.signTx(tx, true);
const txHash = await wallet.submitTx(signedTx);
setResponse(txHash);
} catch (error) {
setResponseError(`${error}`);
}
setLoading(false);
}

let code = ``;
code += `const utxo = await contract.getUtxoByTxHash(txHashToSearchFor);\n`;
code += `const tx = await contract.redeemGiftCard(utxo);\n`;
code += `const signedTx = await wallet.signTx(tx, true);\n`;
code += `const txHash = await wallet.submitTx(signedTx);\n`;

return (
<Card>
<p>
This demo, we will redeem the giftcard that was created in the previous
step. You may connect with another wallet to claim the giftcard
</p>
<Codeblock data={code} isJson={false} />
{connected ? (
<>
<Button
onClick={() => rundemo()}
style={
loading ? 'warning' : response !== null ? 'success' : 'light'
}
disabled={loading || userLocalStorage === undefined}
>
Redeem Giftcard
</Button>
<RunDemoResult response={response} />
</>
) : (
<CardanoWallet />
)}
<RunDemoResult response={responseError} label="Error" />
</Card>
);
}
6 changes: 6 additions & 0 deletions packages/demo/components/site/navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,12 @@ function SubMenuSmartContracts() {
desc="Debt agreement between Lender and Borrower"
icon={<DocumentTextIcon className="w-5 h-5" />}
/>
<SubMenuLinks
href={`/smart-contracts/payment-splitter`}
title="Payment Splitter"
desc="Split contract payouts equally among all payees"
icon={<DocumentTextIcon className="w-5 h-5" />}
/>
</ul>
</div>
</div>
Expand Down

0 comments on commit aeafce6

Please sign in to comment.