diff --git a/sui/operators.js b/sui/operators.js index bf49d0a2..6c31af10 100644 --- a/sui/operators.js +++ b/sui/operators.js @@ -1,35 +1,99 @@ -const { saveConfig, printInfo } = require('../evm/utils'); const { Command, Option } = require('commander'); const { TransactionBlock } = require('@mysten/sui.js/transactions'); +const { bcs } = require('@mysten/sui.js/bcs'); +const { printInfo } = require('../evm/utils'); const { addBaseOptions } = require('./cli-utils'); const { getWallet, printWalletInfo, broadcast } = require('./sign-utils'); const { loadSuiConfig } = require('./utils'); -async function callContractWithCapability(keypair, client, config, chain, args, options) { +async function callContract(keypair, client, config, chain, contractId, functionName, functionArgs, options) { if (!chain.contracts.operators) { throw new Error('Operators package not found.'); } - const contractConfig = chain.contracts.operators; - const packageId = contractConfig.address; - const [contractId, functionName, ...functionArgs] = args; + const operatorsConfig = chain.contracts.operators; + const walletAddress = keypair.getPublicKey().toString(); const tx = new TransactionBlock(); - const [operatorCap] = tx.moveCall({ - target: `${packageId}::operators::borrow_cap`, - arguments: [tx.object(contractConfig.objects.operators), tx.object(contractConfig.objects.operator_cap), tx.pure(options.capId)], - }); + let borrowedCap = null; + + if (options.capId) { + [borrowedCap] = tx.moveCall({ + target: `${operatorsConfig.address}::operators::borrow_cap`, + arguments: [ + tx.object(operatorsConfig.objects.operators), + tx.object(operatorsConfig.objects.operator_caps[walletAddress]), + tx.pure(options.capId), + ], + }); + } + + const callArgs = [...functionArgs]; + + if (options.capIndex !== undefined && borrowedCap) { + callArgs.splice(options.capIndex, 0, borrowedCap); + } tx.moveCall({ target: `${contractId}::${functionName}`, - arguments: [operatorCap, ...functionArgs.map((arg) => tx.pure(arg))], + arguments: callArgs, }); await broadcast(client, keypair, tx); - printInfo('Contract called with borrowed capability'); + printInfo('Contract called successfully'); +} + +async function collectGas(keypair, client, config, chain, args, options) { + if (!chain.contracts.gas_service) { + throw new Error('Gas service package not found.'); + } + + const gasServiceConfig = chain.contracts.gas_service; + const [receiver, amount] = args; + + await callContract( + keypair, + client, + config, + chain, + gasServiceConfig.address, + 'gas_service::collect_gas', + [gasServiceConfig.objects.gas_service, receiver, amount], + { + ...options, + capIndex: 0, + }, + ); + + printInfo('Gas collected successfully'); +} + +async function refundGas(keypair, client, config, chain, args, options) { + if (!chain.contracts.gas_service) { + throw new Error('Gas service package not found.'); + } + + const gasServiceConfig = chain.contracts.gas_service; + const [messageId, receiver, amount] = args; + + await callContract( + keypair, + client, + config, + chain, + gasServiceConfig.address, + 'gas_service::refund', + [gasServiceConfig.objects.gas_service, bcs.string().serialize(messageId).toBytes(), receiver, amount], + { + ...options, + capIndex: 0, + }, + ); + + printInfo('Gas refunded successfully'); } async function mainProcessor(processor, args, options) { @@ -39,8 +103,6 @@ async function mainProcessor(processor, args, options) { await printWalletInfo(keypair, client, config.sui, options); await processor(keypair, client, config, config.sui, args, options); - - saveConfig(config, options.env); } if (require.main === module) { @@ -50,14 +112,30 @@ if (require.main === module) { const callContractCmd = program .command('call-contract [functionArgs...]') - .description('Call a contract with a borrowed capability') + .description('Call a contract with an optional borrowed capability') .addOption(new Option('--capId ', 'ID of the capability to borrow')) - .action((contractId, functionName, functionArgs, options) => - mainProcessor(callContractWithCapability, [contractId, functionName, ...functionArgs], options), - ); + .addOption(new Option('--capIndex ', 'Index of the borrowed capability in the function arguments')) + .action((contractId, functionName, functionArgs, options) => { + options.capIndex = options.capIndex ? parseInt(options.capIndex, 10) : undefined; + mainProcessor(callContract, [contractId, functionName, functionArgs], options); + }); + + const collectGasCmd = program + .command('collect-gas ') + .description('Collect gas from the gas service') + .addOption(new Option('--capId ', 'ID of the GasCollectorCap to borrow')) + .action((receiver, amount, options) => mainProcessor(collectGas, [receiver, amount], options)); + + const refundGasCmd = program + .command('refund-gas ') + .description('Refund gas from the gas service') + .addOption(new Option('--capId ', 'ID of the GasCollectorCap to borrow')) + .action((messageId, receiver, amount, options) => mainProcessor(refundGas, [messageId, receiver, amount], options)); addBaseOptions(program); addBaseOptions(callContractCmd); + addBaseOptions(collectGasCmd); + addBaseOptions(refundGasCmd); program.parse(); }