Implement payments for app deployments #17
@ -5,7 +5,7 @@ import { Octokit } from 'octokit';
|
|||||||
import { inc as semverInc } from 'semver';
|
import { inc as semverInc } from 'semver';
|
||||||
import { DeepPartial } from 'typeorm';
|
import { DeepPartial } from 'typeorm';
|
||||||
|
|
||||||
import { Account, Registry as LaconicRegistry, getGasPrice, parseGasAndFees } from '@cerc-io/registry-sdk';
|
import { Account, Registry as LaconicRegistry, getGasPrice, parseGasAndFees, IndexedTx } from '@cerc-io/registry-sdk';
|
||||||
|
|
||||||
import { RegistryConfig } from './config';
|
import { RegistryConfig } from './config';
|
||||||
import {
|
import {
|
||||||
@ -505,6 +505,15 @@ export class Registry {
|
|||||||
return account.address;
|
return account.address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getTxResponse(txHash: string): Promise<IndexedTx | null> {
|
||||||
|
const account = new Account(Buffer.from(this.registryConfig.privateKey, 'hex'));
|
||||||
|
await account.init();
|
||||||
|
const laconicClient = await this.registry.getLaconicClient(account);
|
||||||
|
const txResponse: IndexedTx | null = await laconicClient.getTx(txHash);
|
||||||
|
|
||||||
|
return txResponse;
|
||||||
|
}
|
||||||
|
|
||||||
getLrn(appName: string): string {
|
getLrn(appName: string): string {
|
||||||
assert(this.registryConfig.authority, "Authority doesn't exist");
|
assert(this.registryConfig.authority, "Authority doesn't exist");
|
||||||
return `lrn://${this.registryConfig.authority}/applications/${appName}`;
|
return `lrn://${this.registryConfig.authority}/applications/${appName}`;
|
||||||
|
@ -84,6 +84,17 @@ export const createResolvers = async (service: Service): Promise<any> => {
|
|||||||
address: async (_: any, __: any, context: any) => {
|
address: async (_: any, __: any, context: any) => {
|
||||||
return service.getAddress();
|
return service.getAddress();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
verifyTx: async (
|
||||||
|
_: any,
|
||||||
|
{
|
||||||
|
txHash,
|
||||||
|
amount,
|
||||||
|
senderAddress,
|
||||||
|
}: { txHash: string; amount: string; senderAddress: string },
|
||||||
|
) => {
|
||||||
|
return service.verifyTx(txHash, amount, senderAddress);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// TODO: Return error in GQL response
|
// TODO: Return error in GQL response
|
||||||
|
@ -265,6 +265,7 @@ type Query {
|
|||||||
domains(projectId: String!, filter: FilterDomainsInput): [Domain]
|
domains(projectId: String!, filter: FilterDomainsInput): [Domain]
|
||||||
deployers: [Deployer]
|
deployers: [Deployer]
|
||||||
address: String!
|
address: String!
|
||||||
|
verifyTx(txhash: String!, amount: String!, senderAddress: String!): Boolean!
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
|
@ -1405,4 +1405,26 @@ export class Service {
|
|||||||
async getAddress(): Promise<any> {
|
async getAddress(): Promise<any> {
|
||||||
return this.laconicRegistry.getAddress();
|
return this.laconicRegistry.getAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async verifyTx(txhash: string, amountSent: string, senderAddress: string): Promise<boolean> {
|
||||||
|
const txResponse = await this.laconicRegistry.getTxResponse(txhash);
|
||||||
|
if (!txResponse) {
|
||||||
|
log('Transaction response not found');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const transfer = txResponse.events.find(e => e.type === 'transfer' && e.attributes.some(a => a.key === 'msg_index'));
|
||||||
|
if (!transfer) {
|
||||||
|
log('No transfer event found');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sender = transfer.attributes.find(a => a.key === 'sender')?.value;
|
||||||
|
const recipient = transfer.attributes.find(a => a.key === 'recipient')?.value;
|
||||||
|
const amount = transfer.attributes.find(a => a.key === 'amount')?.value;
|
||||||
|
|
||||||
|
const recipientAddress = await this.getAddress();
|
||||||
|
|
||||||
|
return amount === amountSent && sender === senderAddress && recipient === recipientAddress;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,6 @@ import {
|
|||||||
ArrowRightCircleFilledIcon,
|
ArrowRightCircleFilledIcon,
|
||||||
LoadingIcon,
|
LoadingIcon,
|
||||||
} from 'components/shared/CustomIcon';
|
} from 'components/shared/CustomIcon';
|
||||||
import { Checkbox } from 'components/shared/Checkbox';
|
|
||||||
import { Button } from 'components/shared/Button';
|
import { Button } from 'components/shared/Button';
|
||||||
import { useToast } from 'components/shared/Toast';
|
import { useToast } from 'components/shared/Toast';
|
||||||
|
|
||||||
@ -169,15 +168,6 @@ const CreateRepo = () => {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<Controller
|
|
||||||
name="isPrivate"
|
|
||||||
control={control}
|
|
||||||
render={({}) => (
|
|
||||||
<Checkbox label="Make this repo private" disabled={true} />
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
{...buttonSize}
|
{...buttonSize}
|
||||||
|
@ -440,4 +440,22 @@ export class GQLClient {
|
|||||||
|
|
||||||
return data.address;
|
return data.address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async verifyTx(txhash: string, amount: string, senderAddress: string): Promise<boolean> {
|
||||||
|
console.log('Verifying Transaction with parameters:', {
|
||||||
|
txhash,
|
||||||
|
amount,
|
||||||
|
senderAddress
|
||||||
|
});
|
||||||
|
const { data } = await this.client.query({
|
||||||
|
query: queries.verifyTx,
|
||||||
|
variables: {
|
||||||
|
txhash,
|
||||||
|
amount,
|
||||||
|
senderAddress
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -329,3 +329,9 @@ query {
|
|||||||
address
|
address
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const verifyTx = gql`
|
||||||
|
query ($txhash: String!, $amount: String!, $senderAddress: String!) {
|
||||||
|
verifyTx(txhash: $txhash, amount: $amount, senderAddress: $senderAddress)
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
Loading…
Reference in New Issue
Block a user