@erc7824/nitrolite
Version:
The Nitrolite SDK empowers developers to build high-performance, scalable web3 applications using state channels. It's designed to provide near-instant transactions and significantly improved user experiences by minimizing direct blockchain interactions.
264 lines (263 loc) • 11.6 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.NitroliteClient = void 0;
const viem_1 = require("viem");
const Errors = __importStar(require("../errors"));
const prepare_1 = require("./prepare");
const services_1 = require("./services");
const state_1 = require("./state");
const CUSTODY_MIN_CHALLENGE_DURATION = 3600n;
class NitroliteClient {
constructor(config) {
if (!config.publicClient)
throw new Errors.MissingParameterError('publicClient');
if (!config.walletClient)
throw new Errors.MissingParameterError('walletClient');
if (!config.walletClient.account)
throw new Errors.MissingParameterError('walletClient.account');
if (!config.challengeDuration)
throw new Errors.MissingParameterError('challengeDuration');
if (config.challengeDuration < CUSTODY_MIN_CHALLENGE_DURATION)
throw new Errors.InvalidParameterError(`The minimum challenge duration is ${CUSTODY_MIN_CHALLENGE_DURATION} seconds`);
if (!config.addresses?.custody)
throw new Errors.MissingParameterError('addresses.custody');
if (!config.addresses?.adjudicator)
throw new Errors.MissingParameterError('addresses.adjudicator');
if (!config.addresses?.guestAddress)
throw new Errors.MissingParameterError('addresses.guestAddress');
if (!config.chainId)
throw new Errors.MissingParameterError('chainId');
this.publicClient = config.publicClient;
this.walletClient = config.walletClient;
this.stateWalletClient = config.stateWalletClient ?? config.walletClient;
this.account = config.walletClient.account;
this.addresses = config.addresses;
this.challengeDuration = config.challengeDuration;
this.chainId = config.chainId;
this.nitroliteService = new services_1.NitroliteService(this.publicClient, this.addresses, this.walletClient, this.account);
this.erc20Service = new services_1.Erc20Service(this.publicClient, this.walletClient);
this.sharedDeps = {
nitroliteService: this.nitroliteService,
erc20Service: this.erc20Service,
addresses: this.addresses,
account: this.account,
walletClient: this.walletClient,
challengeDuration: this.challengeDuration,
stateWalletClient: this.stateWalletClient,
chainId: this.chainId,
};
this.txPreparer = new prepare_1.NitroliteTransactionPreparer(this.sharedDeps);
}
async deposit(tokenAddress, amount) {
const owner = this.account.address;
const spender = this.addresses.custody;
if (tokenAddress !== viem_1.zeroAddress) {
const allowance = await this.erc20Service.getTokenAllowance(tokenAddress, owner, spender);
if (allowance < amount) {
try {
const hash = await this.erc20Service.approve(tokenAddress, spender, amount);
await (0, services_1.waitForTransaction)(this.publicClient, hash);
}
catch (err) {
const error = new Errors.TokenError('Failed to approve tokens for deposit');
throw error;
}
}
}
try {
const depositHash = await this.nitroliteService.deposit(tokenAddress, amount);
await (0, services_1.waitForTransaction)(this.publicClient, depositHash);
return depositHash;
}
catch (err) {
throw new Errors.ContractCallError('Failed to execute deposit on contract', err);
}
}
async createChannel(tokenAddress, params) {
try {
const { channel, initialState, channelId } = await (0, state_1._prepareAndSignInitialState)(tokenAddress, this.sharedDeps, params);
const txHash = await this.nitroliteService.createChannel(channel, initialState);
return { channelId, initialState, txHash };
}
catch (err) {
throw new Errors.ContractCallError('Failed to execute createChannel on contract', err);
}
}
async depositAndCreateChannel(tokenAddress, depositAmount, params) {
try {
const owner = this.account.address;
const spender = this.addresses.custody;
const { channel, initialState, channelId } = await (0, state_1._prepareAndSignInitialState)(tokenAddress, this.sharedDeps, params);
if (tokenAddress !== viem_1.zeroAddress) {
const allowance = await this.erc20Service.getTokenAllowance(tokenAddress, owner, spender);
if (allowance < depositAmount) {
try {
const hash = await this.erc20Service.approve(tokenAddress, spender, depositAmount);
await (0, services_1.waitForTransaction)(this.publicClient, hash);
}
catch (err) {
const error = new Errors.TokenError('Failed to approve tokens for deposit');
throw error;
}
}
}
const txHash = await this.nitroliteService.depositAndCreateChannel(tokenAddress, depositAmount, channel, initialState);
return { channelId, initialState, txHash };
}
catch (err) {
throw new Errors.ContractCallError('Failed to execute depositAndCreateChannel on contract', err);
}
}
async checkpointChannel(params) {
const { channelId, candidateState, proofStates = [] } = params;
if (!candidateState.sigs || candidateState.sigs.length < 2) {
throw new Errors.InvalidParameterError('Candidate state for checkpoint must be signed by both participants.');
}
try {
return await this.nitroliteService.checkpoint(channelId, candidateState, proofStates);
}
catch (err) {
throw new Errors.ContractCallError('Failed to execute checkpointChannel on contract', err);
}
}
async challengeChannel(params) {
const { channelId, candidateState, proofStates = [] } = params;
const { challengerSig } = await (0, state_1._prepareAndSignChallengeState)(this.sharedDeps, params);
try {
return await this.nitroliteService.challenge(channelId, candidateState, proofStates, challengerSig);
}
catch (err) {
throw new Errors.ContractCallError('Failed to execute challengeChannel on contract', err);
}
}
async resizeChannel(params) {
const { resizeStateWithSigs, proofs, channelId } = await (0, state_1._prepareAndSignResizeState)(this.sharedDeps, params);
try {
return await this.nitroliteService.resize(channelId, resizeStateWithSigs, proofs);
}
catch (err) {
throw new Errors.ContractCallError('Failed to execute resizeChannel on contract', err);
}
}
async closeChannel(params) {
try {
const { finalStateWithSigs, channelId } = await (0, state_1._prepareAndSignFinalState)(this.sharedDeps, params);
return await this.nitroliteService.close(channelId, finalStateWithSigs);
}
catch (err) {
throw new Errors.ContractCallError('Failed to execute closeChannel on contract', err);
}
}
async withdrawal(tokenAddress, amount) {
try {
return await this.nitroliteService.withdraw(tokenAddress, amount);
}
catch (err) {
throw new Errors.ContractCallError('Failed to execute withdrawDeposit on contract', err);
}
}
async getOpenChannels() {
try {
return await this.nitroliteService.getOpenChannels(this.account.address);
}
catch (err) {
throw err;
}
}
async getAccountBalance(tokenAddress) {
try {
if (Array.isArray(tokenAddress)) {
return await this.nitroliteService.getAccountBalance(this.account.address, tokenAddress);
}
else {
return await this.nitroliteService.getAccountBalance(this.account.address, tokenAddress);
}
}
catch (err) {
throw err;
}
}
async getChannelBalance(channelId, tokenAddress) {
try {
if (Array.isArray(tokenAddress)) {
return await this.nitroliteService.getChannelBalance(channelId, tokenAddress);
}
else {
return await this.nitroliteService.getChannelBalance(channelId, tokenAddress);
}
}
catch (err) {
throw err;
}
}
async getChannelData(channelId) {
try {
return await this.nitroliteService.getChannelData(channelId);
}
catch (err) {
throw err;
}
}
async approveTokens(tokenAddress, amount) {
const spender = this.addresses.custody;
try {
return await this.erc20Service.approve(tokenAddress, spender, amount);
}
catch (err) {
throw new Errors.TokenError('Failed to approve tokens', undefined, undefined, undefined, undefined, err);
}
}
async getTokenAllowance(tokenAddress) {
const targetOwner = this.account.address;
const targetSpender = this.addresses.custody;
try {
return await this.erc20Service.getTokenAllowance(tokenAddress, targetOwner, targetSpender);
}
catch (err) {
throw new Errors.TokenError('Failed to get token allowance', undefined, undefined, undefined, undefined, err);
}
}
async getTokenBalance(tokenAddress) {
const targetAccount = this.account.address;
try {
return await this.erc20Service.getTokenBalance(tokenAddress, targetAccount);
}
catch (err) {
throw new Errors.TokenError('Failed to get token balance', undefined, undefined, undefined, undefined, err);
}
}
}
exports.NitroliteClient = NitroliteClient;