httpay
Version:
HTTPay SDK for interacting with HTTPay smart contracts on Neutron
540 lines (526 loc) • 19 kB
JavaScript
import { CosmWasmClient, SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate';
import { DirectSecp256k1Wallet } from '@cosmjs/proto-signing';
import { GasPrice } from '@cosmjs/stargate';
/**
* This file was automatically generated by @cosmwasm/ts-codegen@1.12.1.
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
*/
var EscrowTypes = /*#__PURE__*/Object.freeze({
__proto__: null
});
/**
* This file was automatically generated by @cosmwasm/ts-codegen@1.12.1.
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
*/
class EscrowQueryClient {
constructor(client, contractAddress) {
this.getEscrow = async ({ escrowId, }) => {
return this.client.queryContractSmart(this.contractAddress, {
get_escrow: {
escrow_id: escrowId,
},
});
};
this.getCollectedFees = async () => {
return this.client.queryContractSmart(this.contractAddress, {
get_collected_fees: {},
});
};
this.getEscrows = async ({ caller, limit, provider, startAfter, }) => {
return this.client.queryContractSmart(this.contractAddress, {
get_escrows: {
caller,
limit,
provider,
start_after: startAfter,
},
});
};
this.client = client;
this.contractAddress = contractAddress;
this.getEscrow = this.getEscrow.bind(this);
this.getCollectedFees = this.getCollectedFees.bind(this);
this.getEscrows = this.getEscrows.bind(this);
}
}
class EscrowClient extends EscrowQueryClient {
constructor(client, sender, contractAddress) {
super(client, contractAddress);
this.lockFunds = async ({ authToken, expires, maxFee, toolId, }, fee_ = "auto", memo_, funds_) => {
return await this.client.execute(this.sender, this.contractAddress, {
lock_funds: {
auth_token: authToken,
expires,
max_fee: maxFee,
tool_id: toolId,
},
}, fee_, memo_, funds_);
};
this.release = async ({ escrowId, usageFee, }, fee_ = "auto", memo_, funds_) => {
return await this.client.execute(this.sender, this.contractAddress, {
release: {
escrow_id: escrowId,
usage_fee: usageFee,
},
}, fee_, memo_, funds_);
};
this.refundExpired = async ({ escrowId, }, fee_ = "auto", memo_, funds_) => {
return await this.client.execute(this.sender, this.contractAddress, {
refund_expired: {
escrow_id: escrowId,
},
}, fee_, memo_, funds_);
};
this.claimFees = async ({ denom, }, fee_ = "auto", memo_, funds_) => {
return await this.client.execute(this.sender, this.contractAddress, {
claim_fees: {
denom,
},
}, fee_, memo_, funds_);
};
this.client = client;
this.sender = sender;
this.contractAddress = contractAddress;
this.lockFunds = this.lockFunds.bind(this);
this.release = this.release.bind(this);
this.refundExpired = this.refundExpired.bind(this);
this.claimFees = this.claimFees.bind(this);
}
}
var EscrowClient$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
EscrowClient: EscrowClient,
EscrowQueryClient: EscrowQueryClient
});
/**
* This file was automatically generated by @cosmwasm/ts-codegen@1.12.1.
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
*/
var RegistryTypes = /*#__PURE__*/Object.freeze({
__proto__: null
});
/**
* This file was automatically generated by @cosmwasm/ts-codegen@1.12.1.
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
*/
class RegistryQueryClient {
constructor(client, contractAddress) {
this.getTool = async ({ toolId }) => {
return this.client.queryContractSmart(this.contractAddress, {
get_tool: {
tool_id: toolId,
},
});
};
this.getTools = async () => {
return this.client.queryContractSmart(this.contractAddress, {
get_tools: {},
});
};
this.client = client;
this.contractAddress = contractAddress;
this.getTool = this.getTool.bind(this);
this.getTools = this.getTools.bind(this);
}
}
class RegistryClient extends RegistryQueryClient {
constructor(client, sender, contractAddress) {
super(client, contractAddress);
this.registerTool = async ({ denom, description, endpoint, price, toolId, }, fee_ = "auto", memo_, funds_) => {
return await this.client.execute(this.sender, this.contractAddress, {
register_tool: {
denom,
description,
endpoint,
price,
tool_id: toolId,
},
}, fee_, memo_, funds_);
};
this.updatePrice = async ({ price, toolId, }, fee_ = "auto", memo_, funds_) => {
return await this.client.execute(this.sender, this.contractAddress, {
update_price: {
price,
tool_id: toolId,
},
}, fee_, memo_, funds_);
};
this.updateDenom = async ({ denom, toolId, }, fee_ = "auto", memo_, funds_) => {
return await this.client.execute(this.sender, this.contractAddress, {
update_denom: {
denom,
tool_id: toolId,
},
}, fee_, memo_, funds_);
};
this.updateEndpoint = async ({ endpoint, toolId, }, fee_ = "auto", memo_, funds_) => {
return await this.client.execute(this.sender, this.contractAddress, {
update_endpoint: {
endpoint,
tool_id: toolId,
},
}, fee_, memo_, funds_);
};
this.pauseTool = async ({ toolId, }, fee_ = "auto", memo_, funds_) => {
return await this.client.execute(this.sender, this.contractAddress, {
pause_tool: {
tool_id: toolId,
},
}, fee_, memo_, funds_);
};
this.resumeTool = async ({ toolId, }, fee_ = "auto", memo_, funds_) => {
return await this.client.execute(this.sender, this.contractAddress, {
resume_tool: {
tool_id: toolId,
},
}, fee_, memo_, funds_);
};
this.client = client;
this.sender = sender;
this.contractAddress = contractAddress;
this.registerTool = this.registerTool.bind(this);
this.updatePrice = this.updatePrice.bind(this);
this.updateDenom = this.updateDenom.bind(this);
this.updateEndpoint = this.updateEndpoint.bind(this);
this.pauseTool = this.pauseTool.bind(this);
this.resumeTool = this.resumeTool.bind(this);
}
}
var RegistryClient$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
RegistryClient: RegistryClient,
RegistryQueryClient: RegistryQueryClient
});
/**
* High-level HTTPay provider class that simplifies contract interactions
*/
class HTTPayProvider {
constructor(config, tool) {
this.config = config;
this.tool = tool;
}
/**
* Initialize the provider with blockchain connections
*/
async initialize() {
// Create CosmWasm client
this.cosmWasmClient = await CosmWasmClient.connect(this.config.rpcEndpoint);
// Create signing client with provider wallet
const { client, address } = await this.createSigningClient();
this.signingClient = client;
this.providerAddress = address;
}
/**
* Validate payment credentials (escrow ID and auth token)
*/
async validatePayment(payment) {
if (!this.cosmWasmClient) {
throw new Error('HTTPayProvider not initialized. Call initialize() first.');
}
try {
const escrowId = typeof payment.escrowId === 'string'
? parseInt(payment.escrowId, 10)
: payment.escrowId;
if (isNaN(escrowId)) {
return {
isValid: false,
error: 'Invalid escrowId format - must be a number'
};
}
const escrowQueryClient = new EscrowQueryClient(this.cosmWasmClient, this.config.escrowAddress);
const escrowResponse = await escrowQueryClient.getEscrow({
escrowId: escrowId
});
// Validate auth token (simple comparison for now)
if (escrowResponse.auth_token !== payment.authToken) {
return {
isValid: false,
error: 'Invalid authentication token'
};
}
// Check if funds are sufficient (escrow exists means it's active)
const maxFee = parseFloat(escrowResponse.max_fee || '0');
if (maxFee <= 0) {
return {
isValid: false,
error: 'Insufficient funds in escrow'
};
}
return {
isValid: true,
escrow: {
id: escrowId,
provider: escrowResponse.provider || '',
maxFee: escrowResponse.max_fee || '0',
expires: escrowResponse.expires
}
};
}
catch (error) {
return {
isValid: false,
error: error instanceof Error ? error.message : 'Unknown validation error'
};
}
}
/**
* Get tool pricing from registry
*/
async getToolPrice() {
if (!this.cosmWasmClient) {
throw new Error('HTTPayProvider not initialized. Call initialize() first.');
}
try {
const registryQueryClient = new RegistryQueryClient(this.cosmWasmClient, this.config.registryAddress);
const toolResponse = await registryQueryClient.getTool({
toolId: this.tool.toolId
});
if (!toolResponse?.price) {
return { error: 'Tool price not found in registry' };
}
return { price: toolResponse.price };
}
catch (error) {
return {
error: error instanceof Error ? error.message : 'Failed to fetch tool price'
};
}
}
/**
* Process payment by releasing escrow funds
*/
async processPayment(escrowId, usageFee) {
if (!this.signingClient || !this.providerAddress) {
throw new Error('HTTPayProvider not initialized. Call initialize() first.');
}
try {
const escrowClient = new EscrowClient(this.signingClient, this.providerAddress, this.config.escrowAddress);
const result = await escrowClient.release({
escrowId: escrowId,
usageFee: usageFee
}, 'auto');
return {
success: true,
txHash: result.transactionHash,
fee: usageFee
};
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Payment processing failed'
};
}
}
/**
* Complete payment flow: validate + process
*/
async handlePayment(payment) {
// Step 1: Validate payment credentials
const validation = await this.validatePayment(payment);
if (!validation.isValid) {
return { validation };
}
// Step 2: Get tool price
const priceResult = await this.getToolPrice();
if (priceResult.error) {
return {
validation: {
isValid: false,
error: `Failed to get tool price: ${priceResult.error}`
}
};
}
// Step 3: Process payment
const escrowId = typeof payment.escrowId === 'string'
? parseInt(payment.escrowId, 10)
: payment.escrowId;
const processing = await this.processPayment(escrowId, priceResult.price);
return {
validation,
processing,
price: priceResult.price
};
}
/**
* Private helper to create signing client
*/
async createSigningClient() {
const privateKey = this.tool.provider.privateKey;
if (!privateKey || !/^[0-9a-fA-F]{64}$/.test(privateKey)) {
throw new Error('Invalid private key. Must be a 64-character hex string.');
}
const privateKeyBytes = new Uint8Array(privateKey.match(/.{1,2}/g)?.map((byte) => parseInt(byte, 16)) || []);
const wallet = await DirectSecp256k1Wallet.fromKey(privateKeyBytes, 'neutron');
const [providerAccount] = await wallet.getAccounts();
const gasPrice = this.config.gasPrice || '0.025untrn';
const client = await SigningCosmWasmClient.connectWithSigner(this.config.rpcEndpoint, wallet, { gasPrice: GasPrice.fromString(gasPrice) });
return { client, address: providerAccount.address };
}
}
/**
* Environment-based configuration utility
*/
class HTTPayConfigBuilder {
constructor() {
this.config = {};
this.tool = {};
}
/**
* Load configuration from environment variables
*/
static fromEnvironment() {
const builder = new HTTPayConfigBuilder();
// Load HTTPay config from environment
if (process.env.NEXT_PUBLIC_RPC_ENDPOINT) {
builder.config.rpcEndpoint = process.env.NEXT_PUBLIC_RPC_ENDPOINT;
}
if (process.env.NEXT_PUBLIC_REGISTRY_ADDRESS) {
builder.config.registryAddress = process.env.NEXT_PUBLIC_REGISTRY_ADDRESS;
}
if (process.env.NEXT_PUBLIC_ESCROW_ADDRESS) {
builder.config.escrowAddress = process.env.NEXT_PUBLIC_ESCROW_ADDRESS;
}
if (process.env.NEXT_PUBLIC_CHAIN_ID) {
builder.config.chainId = process.env.NEXT_PUBLIC_CHAIN_ID;
}
if (process.env.NEXT_PUBLIC_GAS_PRICE) {
builder.config.gasPrice = process.env.NEXT_PUBLIC_GAS_PRICE;
}
// Load tool config from environment
if (process.env.CLIENT_PRIVATE_KEY) {
builder.tool.provider = {
privateKey: process.env.CLIENT_PRIVATE_KEY
};
}
return builder;
}
/**
* Set RPC endpoint
*/
rpcEndpoint(endpoint) {
this.config.rpcEndpoint = endpoint;
return this;
}
/**
* Set contract addresses
*/
contracts(registryAddress, escrowAddress) {
this.config.registryAddress = registryAddress;
this.config.escrowAddress = escrowAddress;
return this;
}
/**
* Set chain configuration
*/
chain(chainId, gasPrice, gasAdjustment) {
this.config.chainId = chainId;
if (gasPrice)
this.config.gasPrice = gasPrice;
if (gasAdjustment)
this.config.gasAdjustment = gasAdjustment;
return this;
}
/**
* Set tool configuration
*/
setTool(toolId, privateKey) {
this.tool.toolId = toolId;
this.tool.provider = { privateKey };
return this;
}
/**
* Build the final configuration objects
*/
build() {
// Validate required fields
const requiredConfigFields = [
'rpcEndpoint', 'registryAddress', 'escrowAddress'
];
for (const field of requiredConfigFields) {
if (!this.config[field]) {
throw new Error(`Missing required config field: ${field}`);
}
}
if (!this.tool.toolId) {
throw new Error('Missing required tool field: toolId');
}
if (!this.tool.provider?.privateKey) {
throw new Error('Missing required tool field: provider.privateKey');
}
return {
config: {
rpcEndpoint: this.config.rpcEndpoint,
registryAddress: this.config.registryAddress,
escrowAddress: this.config.escrowAddress,
chainId: this.config.chainId || 'pion-1',
gasPrice: this.config.gasPrice || '0.025untrn',
gasAdjustment: this.config.gasAdjustment || 1.3
},
tool: {
toolId: this.tool.toolId,
provider: this.tool.provider
}
};
}
}
/**
* Preset configurations for common networks
*/
const HTTPayPresets = {
neutronTestnet: {
rpcEndpoint: "https://rpc-falcron.pion-1.ntrn.tech",
chainId: "pion-1",
gasPrice: "0.0053untrn",
gasAdjustment: 1.3,
// Contract addresses would need to be provided
registryAddress: "",
escrowAddress: ""
},
neutronMainnet: {
rpcEndpoint: "https://neutron-rpc.publicnode.com",
chainId: "neutron-1",
gasPrice: "0.0053untrn",
gasAdjustment: 1.3,
// Contract addresses would need to be provided
registryAddress: "",
escrowAddress: ""
}
};
/**
* Quick configuration helper
*/
function createHTTPPayConfig(preset, addresses) {
return {
...HTTPayPresets[preset],
...addresses
};
}
/**
* Organized namespace exports for HTTPay SDK (Core only - no React dependencies)
*/
const Escrow = {
...EscrowTypes,
...EscrowClient$1,
};
const Registry = {
...RegistryTypes,
...RegistryClient$1,
};
// Default export with all contracts
var namespace = {
Escrow,
Registry,
};
var namespace$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
Escrow: Escrow,
Registry: Registry,
default: namespace
});
export { EscrowClient, EscrowQueryClient, EscrowTypes, namespace$1 as HTTPay, HTTPayConfigBuilder, HTTPayPresets, HTTPayProvider, RegistryClient, RegistryQueryClient, RegistryTypes, createHTTPPayConfig };
//# sourceMappingURL=index.esm.js.map