@silvana-one/coordination
Version:
Silvana Coordination Client
142 lines • 4.74 kB
JavaScript
import { ParallelTransactionExecutor, } from "@mysten/sui/transactions";
import { suiClient } from "./sui-client.js";
import { nanoid } from "nanoid";
import { sleep } from "./sleep.js";
const executors = {};
const locks = {};
const LOCK_TIMEOUT = 1000 * 60 * 2; // 5 minutes
async function getLock(address) {
const id = nanoid();
let locked = false;
const start = Date.now();
while (!locked && Date.now() - start < LOCK_TIMEOUT) {
if (locks[address]) {
await sleep(Math.floor(Math.random() * 10) + 10);
}
else {
locks[address] = id;
await sleep(10);
if (locks[address] === id) {
locked = true;
return id;
}
}
}
return undefined;
}
async function releaseLock(params) {
const { address, id } = params;
if (locks[address] === id) {
locks[address] = undefined;
}
}
function getExecutor(keyPair) {
const keyPairId = keyPair.toSuiAddress();
if (!executors[keyPairId]) {
executors[keyPairId] = new ParallelTransactionExecutor({
client: suiClient,
signer: keyPair,
initialCoinBalance: 500000000n,
minimumCoinBalance: 300000000n,
maxPoolSize: 5,
});
locks[keyPairId] = undefined;
}
return executors[keyPairId];
}
export async function executeTx(params) {
let lockId;
let address;
try {
const { tx, keyPair, useParallelExecutor = false, showErrors = true, } = params;
let executedTx;
let start = 0;
let end = 0;
if (useParallelExecutor) {
address = keyPair.toSuiAddress();
lockId = await getLock(address);
if (!lockId) {
throw new Error("Failed to get lock");
}
start = Date.now();
const executor = getExecutor(keyPair);
executedTx = (await executor.executeTransaction(tx, {
showEffects: true,
showObjectChanges: true,
showInput: true,
showEvents: true,
showBalanceChanges: true,
})).data;
end = Date.now();
await waitTx(executedTx.digest);
await sleep(1000);
await releaseLock({ address: address, id: lockId });
}
else {
address = keyPair.toSuiAddress();
lockId = await getLock(address);
if (!lockId) {
throw new Error("Failed to get lock");
}
const signedTx = await tx.sign({
signer: keyPair,
client: suiClient,
});
start = Date.now();
// const dryRun = await suiClient.devInspectTransactionBlock({
// sender: keypair.toSuiAddress(),
// transactionBlock: signedTx.bytes
// });
// dryRun.effects.gasUsed.computationCost
// const gasPrice = await suiClient.getReferenceGasPrice();
executedTx = await suiClient.executeTransactionBlock({
transactionBlock: signedTx.bytes,
signature: signedTx.signature,
options: {
showEffects: true,
showObjectChanges: true,
showInput: true,
showEvents: true,
showBalanceChanges: true,
},
});
end = Date.now();
await releaseLock({ address, id: lockId });
}
if (executedTx?.effects?.status?.status === "failure") {
if (showErrors) {
console.log(`Errors for tx ${executedTx.digest}:`, executedTx?.effects?.status?.error);
throw new Error(`tx execution failed: ${executedTx.digest}`);
}
}
return {
tx: executedTx,
digest: executedTx.digest,
events: executedTx?.events?.[0]?.parsedJson,
executeTime: end - start,
};
}
catch (error) {
if (lockId && address) {
await releaseLock({ address, id: lockId });
}
console.error("Error in executeTx", error.message);
return undefined;
}
}
export async function waitTx(digest) {
console.time(`wait sui tx`);
const txWaitResult = await suiClient.waitForTransaction({
digest,
options: {
showEffects: true,
showObjectChanges: true,
showInput: true,
showEvents: true,
showBalanceChanges: true,
},
});
console.timeEnd(`wait sui tx`);
return txWaitResult;
}
//# sourceMappingURL=execute.js.map