@chorus-one/ethereum
Version:
All-in-one toolkit for building staking dApps on Ethereum network
114 lines (113 loc) • 4.15 kB
JavaScript
import { isCompoundingAddress, isStandardAddress } from './utils/addressValidation';
import { http, createPublicClient } from 'viem';
import { hoodi, mainnet } from 'viem/chains';
import { getNetworkConfig } from './utils/getNetworkConfig';
/**
* Connector for interacting with the Native Staking API
* API documentation: https://native-staking.chorus.one/docs
* Used for creating and managing validator batches
* Requires an API token from Chorus One
* Supports Ethereum mainnet and Hoodi testnet
*/
export class NativeStakingConnector {
/** Base URL for Native Staking API */
baseURL;
/** API token for authentication */
apiToken;
/** Network name for API endpoints */
network;
/** Web3 connector for calling read-only contract methods */
eth;
/** Configuration parameters */
config;
constructor(network, apiToken, rpcUrl) {
this.apiToken = apiToken;
this.baseURL = 'https://native-staking.chorus.one';
const transport = rpcUrl ? http(rpcUrl) : http();
switch (network) {
case 'ethereum':
this.network = 'mainnet';
this.eth = createPublicClient({
chain: mainnet,
transport
});
break;
case 'hoodi':
this.network = 'hoodi';
this.eth = createPublicClient({
chain: hoodi,
transport
});
break;
default:
throw new Error(`Invalid network passed: ${network}`);
}
this.config = getNetworkConfig(network);
}
/**
* Makes an authenticated API request to the Native Staking API
*/
async apiRequest(endpoint, options = {}) {
const url = `${this.baseURL}${endpoint}`;
const response = await fetch(url, {
...options,
headers: {
'X-API-TOKEN': this.apiToken,
'Content-Type': 'application/json',
...options.headers
}
});
// 206 is returned when batch is still processing
const isValidStatusCode = response.ok || response.status === 206;
if (!isValidStatusCode) {
const errorText = await response.text();
throw new Error(`Native Staking API error (${response.status}): ${errorText}`);
}
const data = (await response.json());
return data;
}
/**
* Creates a new batch of validators
*/
async createBatch(params) {
const isCompounding = params.is_compounding ?? false;
if (isCompounding) {
if (!isCompoundingAddress(params.withdrawal_address)) {
throw new Error('Compounding validators require withdrawal address in 0x02 format');
}
}
else {
if (!isStandardAddress(params.withdrawal_address)) {
throw new Error('Non-compounding validators require standard withdrawal address format');
}
}
const requestParams = {
batch_id: params.batch_id,
withdrawal_address: params.withdrawal_address,
fee_recipient: params.fee_recipient,
number_of_validators: params.number_of_validators,
network: params.network,
is_compounding: isCompounding,
deposit_gwei_per_validator: params.deposit_gwei_per_validator
};
const endpoint = `/ethereum/${this.network}/batches/new`;
return this.apiRequest(endpoint, {
method: 'POST',
body: JSON.stringify(requestParams)
});
}
/**
* Gets detailed information about a validator batch including validator deposit data
*/
async getBatchDetails(batchId) {
const endpoint = `/ethereum/${this.network}/batches/${batchId}`;
return this.apiRequest(endpoint);
}
/**
* Lists all batches for the authenticated tenant
*/
async listBatches() {
const endpoint = `/ethereum/${this.network}/batches`;
return this.apiRequest(endpoint);
}
}