axie-ronin-ethers-js-tools
Version:
A set of functions that make it easier for developers to interact with their Axies on the Ronin network and the maketplace.
344 lines (303 loc) • 13.1 kB
text/typescript
#!/usr/bin/env node
import { select, input, confirm, password } from '@inquirer/prompts';
import * as dotenv from 'dotenv'
import { ethers, utils } from "ethers";
import { getAxieIdsFromAccount } from "./lib/axie";
import { refreshToken } from "./lib/marketplace/access-token";
import buyMarketplaceOrder from "./lib/marketplace/buy-order";
import cancelMarketplaceOrder from "./lib/marketplace/cancel-order";
import createMarketplaceOrder from "./lib/marketplace/create-order";
import { batchTransferAxies, transferAxie } from "./lib/transfers";
import { getAxieContract, getWETHContract } from "./lib/contracts"
import { MARKETPLACE_GATEWAY_V2 } from "@roninbuilders/contracts"
import { askToContinue, getAxieId } from './lib/utils';
dotenv.config()
async function ensureMarketplaceToken(): Promise<string> {
if (!process.env.MARKETPLACE_ACCESS_TOKEN) {
const token = await input({
message: '🔑 Enter your Marketplace access token:',
validate: (value) => value !== undefined && value !== ''
});
process.env.MARKETPLACE_ACCESS_TOKEN = token;
}
return process.env.MARKETPLACE_ACCESS_TOKEN!;
}
async function main() {
// Ask for Skymavis API key at startup if not in env (optional)
let skyMavisApiKey = process.env.SKYMAVIS_DAPP_KEY;
if (!skyMavisApiKey) {
const shouldProvideKey = await confirm({
message: '🔑 Would you like to provide a Skymavis DApp API key? (Recommended for better rate limits)',
default: false
});
if (shouldProvideKey) {
skyMavisApiKey = await input({
message: '🔑 Enter your Skymavis DApp API key:',
validate: (value) => value !== undefined && value !== ''
});
}
}
// Check for PRIVATE_KEY before the main loop
let privateKey = process.env.PRIVATE_KEY;
if (!privateKey) {
privateKey = await password({
message: '🔐 Enter your private key:',
validate: (value) => {
if (!value) return false;
// Private keys are 32 bytes (64 characters) + optional "0x" prefix
return ethers.utils.isHexString(value, 32) || ethers.utils.isHexString(`0x${value}`, 32);
}
});
// Ensure "0x" prefix
privateKey = privateKey.startsWith('0x') ? privateKey : `0x${privateKey}`;
}
while (true) {
// Initialize provider with the API key from above
const provider = new ethers.providers.JsonRpcProvider(
skyMavisApiKey
? `https://api-gateway.skymavis.com/rpc?apikey=${skyMavisApiKey}`
: "https://api.roninchain.com/rpc"
);
// Initialize wallet with the previously obtained private key
const wallet = new ethers.Wallet(privateKey, provider);
const address = await wallet.getAddress();
try {
const action = await select({
message: 'What would you like to do?',
choices: [
{ name: 'Get account info', value: 'account' },
{ name: 'Refresh access token', value: 'refresh-token' },
{ name: 'Approve WETH', value: 'approve-weth' },
{ name: 'Approve marketplace', value: 'approve-marketplace' },
{ name: 'Buy axie', value: 'buy' },
{ name: 'Delist axie', value: 'delist' },
{ name: 'Delist all axies', value: 'delist-all' },
{ name: 'List axie', value: 'list' },
{ name: 'List all axies', value: 'list-all' },
{ name: 'Transfer axie', value: 'transfer' },
{ name: 'Transfer all axies', value: 'transfer-all' },
]
});
switch (action) {
case 'account': {
const axieIds = await getAxieIdsFromAccount(address, provider);
console.log(`📬 Address: ${address}`);
const wethContract = getWETHContract(wallet)
// Get RON balance
const balance = await provider.getBalance(address);
const balanceInEther = utils.formatEther(balance);
console.log('💰 RON Balance:', balanceInEther);
// Get WETH balance
const wethBalance = await wethContract.balanceOf(address);
const wethBalanceInEther = utils.formatEther(wethBalance);
console.log('💰 WETH Balance:', wethBalanceInEther);
// WETH allowance
const allowance = await wethContract.allowance(address, MARKETPLACE_GATEWAY_V2.address);
console.log('🛒 Marketplace WETH allowance:', !allowance.eq(0) ? '✅ Granted' : '❌ Not granted')
// get axie contract
const axieContract = getAxieContract(provider)
// check if marketplace is approved for axies
const isApprovedForAll = await axieContract.isApprovedForAll(address, MARKETPLACE_GATEWAY_V2.address)
console.log('🔐 Marketplace approval for Axies:', isApprovedForAll ? '✅ Approved' : '❌ Not approved')
console.log(`🐾 Number of Axies: ${axieIds.length}`);
if (axieIds.length > 0) {
console.log(`🆔 Axie IDs: ${axieIds.join(', ')}`);
}
break;
}
case 'refresh-token': {
let refreshTokenValue = process.env.MARKETPLACE_REFRESH_TOKEN;
if (!refreshTokenValue) {
refreshTokenValue = await input({
message: 'Enter refresh token',
validate: (value) => value.length > 0
});
}
const result = await refreshToken(refreshTokenValue);
console.log('New access token:', result.newAccessToken);
console.log('New refresh token:', result.newRefreshToken);
process.env.MARKETPLACE_ACCESS_TOKEN = result.newAccessToken;
process.env.MARKETPLACE_REFRESH_TOKEN = result.newRefreshToken;
break;
}
case 'approve-weth': {
const wethContract = getWETHContract(wallet);
const amountToApprove = '115792089237316195423570985008687907853269984665640564039457584007913129639935';
const address = await wallet.getAddress();
console.log(`Approving Marketplace (${MARKETPLACE_GATEWAY_V2.address}) to spend ${amountToApprove} WETH for the address ${address}`);
const txApproveWETH = await wethContract.approve(MARKETPLACE_GATEWAY_V2.address, amountToApprove, {
gasPrice: utils.parseUnits('20', 'gwei')
});
const txApproveReceipt = await txApproveWETH.wait();
console.log('✅ WETH approved! Transaction hash:', txApproveReceipt.transactionHash);
break;
}
case 'approve-marketplace': {
const axieContract = getAxieContract(wallet);
const address = await wallet.getAddress();
console.log(`Approving Marketplace (${MARKETPLACE_GATEWAY_V2.address}) to handle Axies for ${address}`);
const tx = await axieContract.setApprovalForAll(MARKETPLACE_GATEWAY_V2.address, true, {
gasPrice: utils.parseUnits('20', 'gwei')
});
const receipt = await tx.wait();
console.log('✅ Marketplace approved! Transaction hash:', receipt.transactionHash);
break;
}
case 'buy': {
const token = await ensureMarketplaceToken();
const axieId = await getAxieId();
if (!axieId) break;
const receipt = await buyMarketplaceOrder(
axieId,
wallet,
token,
process.env.SKYMAVIS_DAPP_KEY // This can be undefined
);
if (receipt) {
console.log('🚀 Transaction successful! Hash:', receipt.transactionHash);
console.log('🔗 View transaction: https://app.roninchain.com/tx/' + receipt.transactionHash);
}
break;
}
case 'delist': {
const axieId = await getAxieId();
if (!axieId) break;
const receipt = await cancelMarketplaceOrder(
axieId,
wallet,
process.env.SKYMAVIS_DAPP_KEY // This can be undefined
);
if (receipt) {
console.log('Transaction hash:', receipt.transactionHash);
}
break;
}
case 'delist-all': {
const fromAddress = await wallet.getAddress();
let axieIds = await getAxieIdsFromAccount(fromAddress, provider);
if (axieIds.length > 100) {
console.log('⚠️ Warning: Can only transfer up to 100 Axies at once, only delisting the first 100');
axieIds = axieIds.slice(0, 100);
}
const receipt = await batchTransferAxies(wallet, fromAddress, axieIds);
if (receipt) {
console.log('✅ Axies delisted! Transaction hash:', receipt.transactionHash);
}
break;
}
case 'list': {
const axieId = await getAxieId();
if (!axieId) break;
const token = await ensureMarketplaceToken();
const basePrice = await input({
message: 'Enter base price in ETH',
validate: (value) => utils.parseEther(value).gt(0)
});
const address = await wallet.getAddress();
const currentBlock = await provider.getBlock('latest');
const startedAt = currentBlock.timestamp;
const expiredAt = startedAt + 15634800; // ~6 months
const orderData = {
address,
axieId: axieId.toString(),
basePrice: ethers.utils.parseEther(basePrice).toString(),
endedPrice: '0',
startedAt,
endedAt: 0,
expiredAt,
};
const result = await createMarketplaceOrder(
orderData,
token,
wallet,
process.env.SKYMAVIS_DAPP_KEY // This can be undefined
);
if (result === null || result.errors || !result.data) {
console.error('❌ Error:', result?.errors?.[0]?.message || 'Unknown error');
break;
}
console.log('✅ Order created! Order hash:', result.data.createOrder);
break;
}
case 'list-all': {
const token = await ensureMarketplaceToken();
const basePrice = await input({
message: 'Enter base price in ETH (for all Axies)',
validate: (value) => utils.parseEther(value).gt(0)
});
const address = await wallet.getAddress();
let axieIds = await getAxieIdsFromAccount(address, provider);
if (axieIds.length > 100) {
console.log('⚠️ Warning: Can only list up to 100 Axies at once, only listing the first 100');
axieIds = axieIds.slice(0, 100);
}
const currentBlock = await provider.getBlock('latest');
const startedAt = currentBlock.timestamp;
const expiredAt = startedAt + 15634800; // ~6 months
for (const axieId of axieIds) {
const orderData = {
address,
axieId: axieId.toString(),
basePrice: ethers.utils.parseEther(basePrice).toString(),
endedPrice: '0',
startedAt,
endedAt: 0,
expiredAt,
};
const result = await createMarketplaceOrder(
orderData,
token,
wallet,
process.env.SKYMAVIS_DAPP_KEY
);
if (result === null || result.errors || !result.data) {
console.error(`❌ Error listing Axie ${axieId}:`, result?.errors?.[0]?.message || 'Unknown error');
continue;
}
console.log(`✅ Listed Axie ${axieId}! Order hash:`, result.data.createOrder);
}
break;
}
case 'transfer': {
const axieId = await getAxieId();
if (!axieId) break;
const address = await input({
message: 'Enter recipient address',
validate: (value) => value.length > 0
});
const receipt = await transferAxie(wallet, address, axieId);
if (receipt) {
console.log('✅ Axie transferred! Transaction hash:', receipt.transactionHash);
}
break;
}
case 'transfer-all': {
const address = await input({
message: 'Enter recipient address',
validate: (value) => value.length > 0
});
const fromAddress = await wallet.getAddress();
let axieIds = await getAxieIdsFromAccount(fromAddress, provider);
if (axieIds.length > 100) {
console.log('⚠️ Warning: Can only transfer up to 100 Axies at once, only transfering the first 100');
axieIds = axieIds.slice(0, 100);
}
const receipt = await batchTransferAxies(wallet, address, axieIds);
if (receipt) {
console.log('✅ Axies transferred! Transaction hash:', receipt.transactionHash);
}
break;
}
}
} catch (error) {
if (error instanceof Error) {
console.error('❌ Error:', error.message);
} else {
console.error('❌ Error:', error);
}
} finally {
await askToContinue();
}
}
}
main();