Smart Contract Interaction
Interact directly with the Paycrest Gateway smart contract for onchain stablecoin-to-fiat (offramp) order creation, settlement, and refunds. This approach gives you full control over the blockchain transactions and is ideal for dapps, wallets, or any EVM-compatible application.
Overview
The Gateway contract is a multi-chain EVM-based smart contract that facilitates the on-chain lifecycle of payment orders. It empowers users to create off-ramp orders while enabling liquidity providers to facilitate those orders at competitive exchange rates.
Prerequisites
- Ethereum Provider: MetaMask, WalletConnect, or any Web3 provider
- Ethers.js: For smart contract interactions
- USDT/USDC Balance: Sufficient token balance for orders
- Gas Fees: ETH for transaction fees
Connect Wallet
import { ethers } from "ethers";
// Connect to user's wallet
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const userAddress = await signer.getAddress();
Initialize Contracts
// Gateway contract configuration
const GATEWAY_ADDRESS = "0xE8bc3B607CfE68F47000E3d200310D49041148Fc";
const USDT_ADDRESS = "0xdac17f958d2ee523a2206206994597c13d831ec7";
// Contract instances
const gateway = new ethers.Contract(GATEWAY_ADDRESS, GATEWAY_ABI, signer);
const usdt = new ethers.Contract(USDT_ADDRESS, USDT_ABI, signer);
Create an Order
async function createOffRampOrder(amount, recipient, refundAddress) {
try {
const rate = await getExchangeRate();
const accountName = await verifyAccount(recipient);
const messageHash = await encryptRecipientData({ ...recipient, accountName });
await approveUSDT(amount);
const tx = await gateway.createOrder(
USDT_ADDRESS,
ethers.parseUnits(amount, 6),
rate,
ethers.ZeroAddress,
0,
refundAddress,
messageHash
);
const receipt = await tx.wait();
const orderId = extractOrderId(receipt);
return { orderId, transactionHash: receipt.hash };
} catch (error) {
console.error("Error creating order:", error);
throw error;
}
}
Exchange Rate and Account Verification
async function getExchangeRate() {
const response = await fetch("https://api.paycrest.io/v1/rates/usdt/1/ngn");
const data = await response.json();
return Number(data.data);
}
async function verifyAccount(recipient) {
const response = await fetch("https://api.paycrest.io/v1/verify-account", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
institution: recipient.institution,
accountIdentifier: recipient.accountIdentifier
})
});
const data = await response.json();
return data.data;
}
Data Encryption
async function encryptRecipientData(recipient) {
const response = await fetch("https://api.paycrest.io/v1/pubkey");
const { data: publicKey } = await response.json();
const encrypt = new JSEncrypt();
encrypt.setPublicKey(publicKey);
const encrypted = encrypt.encrypt(JSON.stringify(recipient));
if (!encrypted) {
throw new Error("Failed to encrypt recipient data");
}
return encrypted;
}
Token Approval
async function approveUSDT(amount) {
const usdtAmount = ethers.parseUnits(amount, 6);
const allowance = await usdt.allowance(userAddress, GATEWAY_ADDRESS);
if (allowance.lt(usdtAmount)) {
const tx = await usdt.approve(GATEWAY_ADDRESS, usdtAmount);
await tx.wait();
}
}
async function getOrderInfo(orderId) {
const orderInfo = await gateway.getOrderInfo(orderId);
return {
sender: orderInfo.sender,
token: orderInfo.token,
amount: ethers.formatUnits(orderInfo.amount, 6),
isFulfilled: orderInfo.isFulfilled,
isRefunded: orderInfo.isRefunded,
refundAddress: orderInfo.refundAddress
};
}
Event Listening
gateway.on("OrderCreated", (sender, token, amount, protocolFee, orderId, rate, messageHash) => {
console.log("Order Created:", {
sender,
token,
amount: ethers.formatUnits(amount, 6),
orderId,
rate
});
});
gateway.on("OrderSettled", (splitOrderId, orderId, liquidityProvider, settlePercent) => {
console.log("Order Settled:", { orderId, liquidityProvider, settlePercent });
});
gateway.on("OrderRefunded", (fee, orderId) => {
console.log("Order Refunded:", {
fee: ethers.formatUnits(fee, 6),
orderId
});
});
Error Handling
function handleContractError(error) {
if (error.code === 4001) {
throw new Error("User rejected transaction");
} else if (error.message.includes("insufficient funds")) {
throw new Error("Insufficient USDT balance or ETH for gas");
} else if (error.message.includes("execution reverted")) {
throw new Error("Transaction reverted - check parameters");
} else {
throw new Error(`Contract error: ${error.message}`);
}
}
async function retryTransaction(txFunction, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await txFunction();
} catch (error) {
if (attempt === maxRetries) throw error;
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
}
}
}
Complete Example
class PaycrestGateway {
constructor(provider, gatewayAddress, usdtAddress) {
this.provider = provider;
this.gatewayAddress = gatewayAddress;
this.usdtAddress = usdtAddress;
this.gateway = null;
this.usdt = null;
}
async initialize() {
const signer = await this.provider.getSigner();
this.gateway = new ethers.Contract(this.gatewayAddress, GATEWAY_ABI, signer);
this.usdt = new ethers.Contract(this.usdtAddress, USDT_ABI, signer);
this.setupEventListeners();
}
setupEventListeners() {
this.gateway.on("OrderCreated", this.handleOrderCreated.bind(this));
this.gateway.on("OrderSettled", this.handleOrderSettled.bind(this));
this.gateway.on("OrderRefunded", this.handleOrderRefunded.bind(this));
}
async createOrder(amount, recipient, refundAddress) {
try {
const rate = await this.getExchangeRate();
const accountName = await this.verifyAccount(recipient);
const messageHash = await this.encryptRecipientData({ ...recipient, accountName });
await this.approveUSDT(amount);
const tx = await this.gateway.createOrder(
this.usdtAddress,
ethers.parseUnits(amount, 6),
rate,
ethers.ZeroAddress,
0,
refundAddress,
messageHash
);
const receipt = await tx.wait();
return { orderId: this.extractOrderId(receipt), hash: receipt.hash };
} catch (error) {
handleContractError(error);
}
}
// ... other methods (getExchangeRate, verifyAccount, etc.)
}
// Usage
const provider = new ethers.BrowserProvider(window.ethereum);
const paycrest = new PaycrestGateway(
provider,
"0xE8bc3B607CfE68F47000E3d200310D49041148Fc",
"0xdac17f958d2ee523a2206206994597c13d831ec7"
);
await paycrest.initialize();
const order = await paycrest.createOrder(
"100", // USDT amount
{
institution: "GTBINGLA",
accountIdentifier: "123456789"
},
"0x123..." // Refund address
);
Supported Networks
- Base: Primary network for USDT/USDC transactions
- Polygon: Cost-effective transactions
- BNB Smart Chain: Binance ecosystem support
- Arbitrum One: High-performance L2 network
- Lisk: Alternative blockchain network
- Celo: Mobile-first blockchain (CUSD, CNGN)
- Tron: USDT transactions
Paycrest supports very low minimum orders ($0.50) and uses cost-effective EVM L2s. Start with small amounts to test your integration before scaling up.
Choose this method for full onchain control and direct smart contract interaction.
Responses are generated using AI and may contain mistakes.