Implement payments for app deployments #17

Merged
nabarun merged 27 commits from iv-integrate-payments into main 2024-10-28 09:46:19 +00:00
3 changed files with 104 additions and 85 deletions
Showing only changes of commit 1af5d64b1f - Show all commits

View File

@ -85,16 +85,16 @@ export const createResolvers = async (service: Service): Promise<any> => {
return service.getAddress(); return service.getAddress();
}, },
verifyTx: async ( // verifyTx: async (
_: any, // _: any,
{ // {
txHash, // txHash,
amount, // amount,
senderAddress, // senderAddress,
}: { txHash: string; amount: string; senderAddress: string }, // }: { txHash: string; amount: string; senderAddress: string },
) => { // ) => {
return service.verifyTx(txHash, amount, senderAddress); // return service.verifyTx(txHash, amount, senderAddress);
}, // },
}, },
// TODO: Return error in GQL response // TODO: Return error in GQL response

View File

@ -23,6 +23,7 @@ import { useGQLClient } from '../../../context/GQLClientContext';
import EnvironmentVariablesForm from 'pages/org-slug/projects/id/settings/EnvironmentVariablesForm'; import EnvironmentVariablesForm from 'pages/org-slug/projects/id/settings/EnvironmentVariablesForm';
import { EnvironmentVariablesFormValues } from 'types/types'; import { EnvironmentVariablesFormValues } from 'types/types';
import ConnectWallet from './ConnectWallet'; import ConnectWallet from './ConnectWallet';
import { useWalletConnectClient } from 'context/WalletConnectContext';
type ConfigureDeploymentFormValues = { type ConfigureDeploymentFormValues = {
option: string; option: string;
@ -35,8 +36,11 @@ type ConfigureFormValues = ConfigureDeploymentFormValues &
EnvironmentVariablesFormValues; EnvironmentVariablesFormValues;
const Configure = () => { const Configure = () => {
const { signClient, session } = useWalletConnectClient();
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [deployers, setDeployers] = useState<Deployer[]>([]); const [deployers, setDeployers] = useState<Deployer[]>([]);
const [selectedAccount, setSelectedAccount] = useState<string>();
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const templateId = searchParams.get('templateId'); const templateId = searchParams.get('templateId');
@ -148,12 +152,41 @@ const Configure = () => {
} }
}; };
// const verifyTx = async (
// senderAddress: string,
// txHash: string,
// ): Promise<boolean> => {
// const isValid = await client.verifyTx(
// txHash,
// `${amount.toString()}alnt`,
// senderAddress,
// );
// return isValid;
// };
const handleFormSubmit = useCallback( const handleFormSubmit = useCallback(
async (createFormData: FieldValues) => { async (createFormData: FieldValues) => {
// Send tx request to wallet -> amount = createFormData.maxPrice * createFormData.numProviders // Send tx request to wallet -> amount = createFormData.maxPrice * createFormData.numProviders
// Get address of sender account(from wallet connect session) and txHash(result.signature) // Get address of sender account(from wallet connect session) and txHash(result.signature)
const senderAddress = 'address'; if (!selectedAccount) {
const txHash = 'txHash'; return;
}
const senderAddress = selectedAccount;
const amount = createFormData.numProviders * createFormData.maxPrice;
const txHash = await cosmosSendTokensHandler(
selectedAccount,
String(amount),
);
console.log(txHash);
// const isTxHashValid = verifyTx(senderAddress, txHash);
// if (!isTxHashValid) {
// console.error("Invalid Tx hash", txHash)
// return
// }
const environmentVariables = createFormData.variables.map( const environmentVariables = createFormData.variables.map(
(variable: any) => { (variable: any) => {
@ -171,7 +204,7 @@ const Configure = () => {
createFormData, createFormData,
environmentVariables, environmentVariables,
senderAddress, senderAddress,
txHash, txHash!,
); );
await client.getEnvironmentVariables(projectId); await client.getEnvironmentVariables(projectId);
@ -202,6 +235,48 @@ const Configure = () => {
setDeployers(res.deployers); setDeployers(res.deployers);
}, [client]); }, [client]);
const onAccountChange = useCallback((account: string) => {
setSelectedAccount(account);
}, []);
const cosmosSendTokensHandler = useCallback(
async (selectedAccount: string, amount: string) => {
if (!signClient || !session || !selectedAccount) {
return;
}
const chainId = selectedAccount.split(':')[1];
const senderAddress = selectedAccount.split(':')[2];
const snowballAddress = await client.getAddress();
try {
const result: { signature: string } = await signClient.request({
topic: session.topic,
chainId: `cosmos:${chainId}`,
request: {
method: 'cosmos_sendTokens',
params: [
{
from: senderAddress,
to: snowballAddress,
value: amount,
},
],
},
});
if (!result) {
throw new Error('Error completing transaction');
}
return result.signature;
} catch (error: any) {
throw error;
}
},
[session, signClient],
);
useEffect(() => { useEffect(() => {
fetchDeployers(); fetchDeployers();
}, []); }, []);
@ -340,7 +415,10 @@ const Configure = () => {
<div className="p-4 bg-slate-100 rounded-lg mb-6"> <div className="p-4 bg-slate-100 rounded-lg mb-6">
<EnvironmentVariablesForm /> <EnvironmentVariablesForm />
</div> </div>
<Heading as="h4" className="md:text-lg font-medium mb-3">
Connect to your wallet
</Heading>
<ConnectWallet onAccountChange={onAccountChange} />
<div> <div>
<Button <Button
{...buttonSize} {...buttonSize}
@ -359,7 +437,6 @@ const Configure = () => {
</div> </div>
</form> </form>
</FormProvider> </FormProvider>
<ConnectWallet numProviders={methods.watch('numProviders') || 0} />
</div> </div>
</div> </div>
); );

View File

@ -1,86 +1,33 @@
import { useMemo, useState, useCallback } from 'react';
import { Select, Option } from '@snowballtools/material-tailwind-react-fork'; import { Select, Option } from '@snowballtools/material-tailwind-react-fork';
import { Button } from '../../shared/Button'; import { Button } from '../../shared/Button';
import { useWalletConnectClient } from 'context/WalletConnectContext'; import { useWalletConnectClient } from 'context/WalletConnectContext';
import { useGQLClient } from 'context/GQLClientContext';
const ConnectWallet = ({ numProviders }: { numProviders: number }) => { const ConnectWallet = ({
const { onConnect, accounts, signClient, session } = useWalletConnectClient(); onAccountChange,
const client = useGQLClient(); }: {
onAccountChange: (selectedAccount: string) => void;
const [selectedAccount, setSelectedAccount] = useState<string>(); }) => {
const [isTxValid, setIsTxValid] = useState<boolean>(false); const { onConnect, accounts } = useWalletConnectClient();
const amount = useMemo(() => numProviders * 10000, [numProviders]);
const handleConnect = async () => { const handleConnect = async () => {
await onConnect(); await onConnect();
}; };
const verifyTx = async (
senderAddress: string,
txHash: string,
): Promise<boolean> => {
const isValid = await client.verifyTx(
txHash,
`${amount.toString()}alnt`,
senderAddress,
);
return isValid;
};
const cosmosSendTokensHandler = useCallback(
async (selectedAccount: string) => {
if (!signClient || !session || !selectedAccount) {
return;
}
const chainId = selectedAccount.split(':')[1];
const senderAddress = selectedAccount.split(':')[2];
const snowballAddress = await client.getAddress();
try {
const result: { signature: string } = await signClient.request({
topic: session.topic,
chainId: `cosmos:${chainId}`,
request: {
method: 'cosmos_sendTokens',
params: [
{
from: senderAddress,
to: snowballAddress,
value: amount,
},
],
},
});
if (!result) {
throw new Error('Error completing transaction');
}
const isValid = await verifyTx(senderAddress, result.signature);
setIsTxValid(isValid);
} catch (error: any) {
throw error;
}
},
[session, signClient, selectedAccount, amount],
);
return ( return (
<div className="p-4 bg-slate-100 rounded-lg mb-6"> <div className="p-4 bg-slate-100 rounded-lg mb-6">
{!accounts ? ( {!accounts ? (
<Button onClick={handleConnect}>Connect Wallet</Button> <div>
) : isTxValid ? ( <Button type={'button'} onClick={handleConnect}>
<div className="mt-4 text-green-600">Tx successful!</div> Connect Wallet
</Button>
</div>
) : ( ) : (
<div> <div>
<Select <Select
label="Select Account" label="Select Account"
defaultValue={accounts[0].address} defaultValue={accounts[0].address}
onChange={(value) => { onChange={(value) => {
setSelectedAccount(value); value && onAccountChange(value);
}} }}
> >
{accounts.map((account, index) => ( {accounts.map((account, index) => (
@ -89,11 +36,6 @@ const ConnectWallet = ({ numProviders }: { numProviders: number }) => {
</Option> </Option>
))} ))}
</Select> </Select>
<Button
onClick={() => cosmosSendTokensHandler(selectedAccount || '')}
>
Pay
</Button>
</div> </div>
)} )}
</div> </div>