Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support to generate unsigned txs in sui scripts #442

Merged
merged 3 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 1 addition & 12 deletions sui/deploy-contract.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
const { Command, Option } = require('commander');
const { getLocalDependencies, updateMoveToml, TxBuilder, bcsStructs } = require('@axelar-network/axelar-cgp-sui');
const { toB64 } = require('@mysten/sui/utils');
const { bcs } = require('@mysten/sui/bcs');
const { Transaction } = require('@mysten/sui/transactions');
const { saveConfig, printInfo, validateParameters, writeJSON, getDomainSeparator, loadConfig, getChainConfig } = require('../common');
const { saveConfig, printInfo, validateParameters, getDomainSeparator, loadConfig, getChainConfig } = require('../common');
const {
addBaseOptions,
addOptionsToCommands,
Expand Down Expand Up @@ -342,16 +341,6 @@ async function mainProcessor(args, options, processor) {
await processor(keypair, client, ...args, config, sui, options);

saveConfig(config, options.env);

if (options.offline) {
const { txFilePath } = options;
validateParameters({ isNonEmptyString: { txFilePath } });

const txB64Bytes = toB64(options.txBytes);

writeJSON({ message: options.offlineMessage, status: 'PENDING', unsignedTx: txB64Bytes }, txFilePath);
printInfo(`Unsigned transaction`, txFilePath);
}
}

/**
Expand Down
43 changes: 38 additions & 5 deletions sui/gateway.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const {
getRawPrivateKey,
broadcast,
suiClockAddress,
saveGeneratedTx,
} = require('./utils');
const secp256k1 = require('secp256k1');

Expand Down Expand Up @@ -140,7 +141,10 @@ async function callContract(keypair, client, config, chain, contractConfig, args
});
}

await broadcast(client, keypair, tx, 'Message sent');
return {
tx,
message: 'Message sent',
};
}

async function approve(keypair, client, config, chain, contractConfig, args, options) {
Expand Down Expand Up @@ -173,7 +177,10 @@ async function approve(keypair, client, config, chain, contractConfig, args, opt
],
});

await broadcast(client, keypair, tx, 'Approved Messages');
return {
tx,
message: 'Approved Messages',
};
}

async function submitProof(keypair, client, config, chain, contractConfig, args, options) {
Expand Down Expand Up @@ -216,7 +223,10 @@ async function submitProof(keypair, client, config, chain, contractConfig, args,
throw new Error(`Unknown payload type: ${payload}`);
}

await broadcast(client, keypair, tx, 'Submitted Amplifier Proof');
return {
tx,
message: 'Submitted Amplifier Proof',
};
}

async function rotate(keypair, client, config, chain, contractConfig, args, options) {
Expand All @@ -243,7 +253,10 @@ async function rotate(keypair, client, config, chain, contractConfig, args, opti
],
});

await broadcast(client, keypair, tx, 'Rotated Signers');
return {
tx,
message: 'Rotated Signers',
};
}

async function mainProcessor(processor, args, options) {
Expand All @@ -257,9 +270,17 @@ async function mainProcessor(processor, args, options) {
throw new Error('Axelar Gateway package not found.');
}

await processor(keypair, client, config, chain, chain.contracts.AxelarGateway, args, options);
const { tx, message } = await processor(keypair, client, config, chain, chain.contracts.AxelarGateway, args, options);

saveConfig(config, options.env);

if (options.offline) {
const sender = options.sender || keypair.toSuiAddress();
tx.setSender(sender);
await saveGeneratedTx(tx, message, client, options);
} else {
await broadcast(client, keypair, tx, message);
}
}

if (require.main === module) {
Expand All @@ -274,6 +295,9 @@ if (require.main === module) {
.addOption(new Option('--proof <proof>', 'JSON of the proof'))
.addOption(new Option('--currentNonce <currentNonce>', 'nonce of the existing signers'))
.addOption(new Option('--newNonce <newNonce>', 'nonce of the new signers (useful for test rotations)'))
.addOption(new Option('--sender <sender>', 'transaction sender'))
.addOption(new Option('--offline', 'store tx block for sign'))
.addOption(new Option('--txFilePath <file>', 'unsigned transaction will be stored'))
.action((options) => {
mainProcessor(rotate, [], options);
});
Expand All @@ -283,13 +307,19 @@ if (require.main === module) {
.description('Approve messages at the gateway contract')
.addOption(new Option('--proof <proof>', 'JSON of the proof'))
.addOption(new Option('--currentNonce <currentNonce>', 'nonce of the existing signers'))
.addOption(new Option('--sender <sender>', 'transaction sender'))
.addOption(new Option('--offline', 'store tx block for sign'))
.addOption(new Option('--txFilePath <file>', 'unsigned transaction will be stored'))
.action((sourceChain, messageId, sourceAddress, destinationId, payloadHash, options) => {
mainProcessor(approve, [sourceChain, messageId, sourceAddress, destinationId, payloadHash], options);
});

program
.command('submitProof <multisigSessionId>')
.description('Submit proof for the provided amplifier multisig session id')
.addOption(new Option('--sender <sender>', 'transaction sender'))
.addOption(new Option('--offline', 'store tx block for sign'))
.addOption(new Option('--txFilePath <file>', 'unsigned transaction will be stored'))
.action((multisigSessionId, options) => {
mainProcessor(submitProof, [multisigSessionId], options);
});
Expand All @@ -298,6 +328,9 @@ if (require.main === module) {
.command('call-contract <destinationChain> <destinationAddress> <payload>')
.description('Initiate sending a cross-chain message via the gateway')
.addOption(new Option('--channel <channel>', 'Existing channel ID to initiate a cross-chain message over'))
.addOption(new Option('--sender <sender>', 'transaction sender'))
.addOption(new Option('--offline', 'store tx block for sign'))
.addOption(new Option('--txFilePath <file>', 'unsigned transaction will be stored'))
.action((destinationChain, destinationAddress, payload, options) => {
mainProcessor(callContract, [destinationChain, destinationAddress, payload], options);
});
Expand Down
24 changes: 17 additions & 7 deletions sui/its.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const { Command } = require('commander');
const { Command, Option } = require('commander');
const { TxBuilder } = require('@axelar-network/axelar-cgp-sui');
const { loadConfig, saveConfig, getChainConfig } = require('../common/utils');
const { addBaseOptions, addOptionsToCommands, getWallet, printWalletInfo, broadcastFromTxBuilder } = require('./utils');
const { addBaseOptions, addOptionsToCommands, getWallet, printWalletInfo, broadcastFromTxBuilder, saveGeneratedTx } = require('./utils');

async function setupTrustedAddress(keypair, client, contracts, args, options) {
const [trustedChain, trustedAddress] = args;
Expand All @@ -22,13 +22,20 @@ async function setupTrustedAddress(keypair, client, contracts, args, options) {
arguments: [ITS, OwnerCap, trustedAddressesObject],
});

await broadcastFromTxBuilder(txBuilder, keypair, 'Setup Trusted Address');
if (options.offline) {
const tx = txBuilder.tx;
const sender = options.sender || keypair.toSuiAddress();
tx.setSender(sender);
await saveGeneratedTx(tx, `Set trusted address for ${trustedChain} to ${trustedAddress}`, client, options);
} else {
await broadcastFromTxBuilder(txBuilder, keypair, 'Setup Trusted Address');

// Add trusted address to ITS config
if (!contracts.ITS.trustedAddresses) contracts.ITS.trustedAddresses = {};
if (!contracts.ITS.trustedAddresses[trustedChain]) contracts.ITS.trustedAddresses[trustedChain] = [];
// Add trusted address to ITS config
if (!contracts.ITS.trustedAddresses) contracts.ITS.trustedAddresses = {};
if (!contracts.ITS.trustedAddresses[trustedChain]) contracts.ITS.trustedAddresses[trustedChain] = [];

contracts.ITS.trustedAddresses[trustedChain].push(trustedAddress);
contracts.ITS.trustedAddresses[trustedChain].push(trustedAddress);
}
}

async function processCommand(command, chain, args, options) {
Expand Down Expand Up @@ -56,6 +63,9 @@ if (require.main === module) {
.name('setup-trusted-address')
.description('Setup trusted address')
.command('setup-trusted-address <trusted-chain> <trusted-address>')
.addOption(new Option('--offline', 'store tx block for offline signing'))
.addOption(new Option('--sender <sender>', 'transaction sender'))
.addOption(new Option('--txFilePath <file>', 'unsigned transaction will be stored'))
.action((trustedChain, trustedAddress, options) => {
blockchainguyy marked this conversation as resolved.
Show resolved Hide resolved
mainProcessor(setupTrustedAddress, options, [trustedChain, trustedAddress], processCommand);
});
Expand Down
66 changes: 54 additions & 12 deletions sui/operators.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const { Command, Option } = require('commander');
const { Transaction } = require('@mysten/sui/transactions');
const { printError, loadConfig, getChainConfig } = require('../common/utils');
const { loadConfig, getChainConfig } = require('../common/utils');
const {
addBaseOptions,
addOptionsToCommands,
Expand All @@ -9,6 +9,7 @@ const {
printWalletInfo,
broadcast,
findOwnedObjectIdByType,
saveGeneratedTx,
} = require('./utils');

function operatorMoveCall(contractConfig, gasServiceConfig, operatorCapId, tx, moveCall) {
Expand Down Expand Up @@ -50,7 +51,10 @@ async function collectGas(keypair, client, gasServiceConfig, contractConfig, arg
});
});

await broadcast(client, keypair, tx, 'Gas Collected');
return {
tx,
message: 'Gas Collected',
};
}

async function refund(keypair, client, gasServiceConfig, contractConfig, args, options) {
Expand Down Expand Up @@ -78,7 +82,10 @@ async function refund(keypair, client, gasServiceConfig, contractConfig, args, o
});
});

await broadcast(client, keypair, tx, 'Refunded Gas');
return {
tx,
message: 'Refunded Gas',
};
}

async function storeCap(keypair, client, gasServiceConfig, contractConfig, args, options) {
Expand All @@ -95,7 +102,10 @@ async function storeCap(keypair, client, gasServiceConfig, contractConfig, args,
typeArguments: [`${gasServiceConfig.address}::gas_service::GasCollectorCap`],
});

await broadcast(client, keypair, tx, 'Stored Capability');
return {
tx,
message: 'Stored Capability',
};
}

async function addOperator(keypair, client, gasServiceConfig, contractConfig, args, options) {
Expand All @@ -111,7 +121,10 @@ async function addOperator(keypair, client, gasServiceConfig, contractConfig, ar
arguments: [tx.object(operatorsObjectId), tx.object(ownerCapObjectId), tx.pure.address(newOperatorAddress)],
});

await broadcast(client, keypair, tx, 'Added Operator');
return {
tx,
message: 'Added Operator',
};
}

async function removeCap(keypair, client, gasServiceConfig, contractConfig, args, options) {
Expand All @@ -132,11 +145,10 @@ async function removeCap(keypair, client, gasServiceConfig, contractConfig, args

tx.transferObjects([cap], capReceiver);

try {
await broadcast(client, keypair, tx, 'Removed Capability');
} catch (e) {
printError('RemoveCap Error', e.message);
}
return {
tx,
message: 'Removed Capability',
};
}

async function removeOperator(keypair, client, gasServiceConfig, contractConfig, args, options) {
Expand All @@ -152,7 +164,10 @@ async function removeOperator(keypair, client, gasServiceConfig, contractConfig,
arguments: [tx.object(operatorsObjectId), tx.object(ownerCapObjectId), tx.pure.address(operatorAddress)],
});

await broadcast(client, keypair, tx, 'Removed Operator');
return {
tx,
message: 'Removed Operator',
};
}

async function mainProcessor(processor, args, options) {
Expand All @@ -172,7 +187,16 @@ async function mainProcessor(processor, args, options) {

const [keypair, client] = getWallet(chain, options);
await printWalletInfo(keypair, client, chain, options);
await processor(keypair, client, gasServiceConfig, contractConfig, args, options);

const { tx, message } = await processor(keypair, client, gasServiceConfig, contractConfig, args, options);

if (options.offline) {
const sender = options.sender || keypair.toSuiAddress();
tx.setSender(sender);
await saveGeneratedTx(tx, message, client, options);
} else {
await broadcast(client, keypair, tx, message);
}
}

if (require.main === module) {
Expand All @@ -185,6 +209,9 @@ if (require.main === module) {
.command('add <newOperatorAddress>')
.description('Add an operator')
.addOption(new Option('--ownerCap <ownerCapId>', 'ID of the owner capability'))
.addOption(new Option('--sender <sender>', 'transaction sender'))
.addOption(new Option('--offline', 'store tx block for sign'))
.addOption(new Option('--txFilePath <file>', 'unsigned transaction will be stored'))
.action((newOperatorAddress, options) => mainProcessor(addOperator, [newOperatorAddress], options)),
);

Expand All @@ -193,6 +220,9 @@ if (require.main === module) {
.command('remove <operatorAddress>')
.description('Remove an operator')
.addOption(new Option('--ownerCap <ownerCapId>', 'ID of the owner capability'))
.addOption(new Option('--sender <sender>', 'transaction sender'))
.addOption(new Option('--offline', 'store tx block for sign'))
.addOption(new Option('--txFilePath <file>', 'unsigned transaction will be stored'))
.action((operatorAddress, options) => mainProcessor(removeOperator, [operatorAddress], options)),
);

Expand All @@ -201,6 +231,9 @@ if (require.main === module) {
.command('collectGas')
.description('Collect gas from the gas service')
.addOption(new Option('--receiver <receiver>', 'Address of the receiver'))
.addOption(new Option('--sender <sender>', 'transaction sender'))
.addOption(new Option('--offline', 'store tx block for sign'))
.addOption(new Option('--txFilePath <file>', 'unsigned transaction will be stored'))
.requiredOption('--amount <amount>', 'Amount to add gas', parseSuiUnitAmount)
.action((options) => mainProcessor(collectGas, [options.amount], options)),
);
Expand All @@ -210,6 +243,9 @@ if (require.main === module) {
.command('storeCap')
.description('Store a capability')
.addOption(new Option('--capId <capId>', 'ID of the capability to store'))
.addOption(new Option('--sender <sender>', 'transaction sender'))
.addOption(new Option('--offline', 'store tx block for sign'))
.addOption(new Option('--txFilePath <file>', 'unsigned transaction will be stored'))
.action((options) => mainProcessor(storeCap, [], options)),
);

Expand All @@ -219,6 +255,9 @@ if (require.main === module) {
.description('Remove a capability')
.addOption(new Option('--ownerCap <ownerCapId>', 'ID of the owner capability'))
.addOption(new Option('--receiver <receiver>', 'The removed cap receiver address'))
.addOption(new Option('--sender <sender>', 'transaction sender'))
.addOption(new Option('--offline', 'store tx block for sign'))
.addOption(new Option('--txFilePath <file>', 'unsigned transaction will be stored'))
.action((capId, options) => mainProcessor(removeCap, [capId], options)),
);

Expand All @@ -228,6 +267,9 @@ if (require.main === module) {
.description('Refund gas from the gas service')
.addOption(new Option('--receiver <receiver>', 'Address of the receiver'))
.requiredOption('--amount <amount>', 'Amount to refund', parseSuiUnitAmount)
.addOption(new Option('--sender <sender>', 'transaction sender'))
.addOption(new Option('--offline', 'store tx block for sign'))
.addOption(new Option('--txFilePath <file>', 'unsigned transaction will be stored'))
.action((messageId, options) => mainProcessor(refund, [messageId], options)),
);

Expand Down
13 changes: 11 additions & 2 deletions sui/transfer-object.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const { Transaction } = require('@mysten/sui/transactions');
const { Command, Option } = require('commander');
const { loadConfig, validateParameters, getChainConfig } = require('../common/utils');
const { getWallet, printWalletInfo, addExtendedOptions, broadcast } = require('./utils');
const { getWallet, printWalletInfo, addExtendedOptions, broadcast, saveGeneratedTx } = require('./utils');

async function processCommand(chain, options) {
const [keypair, client] = getWallet(chain, options);
Expand Down Expand Up @@ -38,7 +38,13 @@ async function processCommand(chain, options) {
const tx = new Transaction();
tx.transferObjects([`${objectId}`], tx.pure.address(recipient));

await broadcast(client, keypair, tx, 'Transferred Object');
if (options.offline) {
const sender = options.sender || keypair.toSuiAddress();
tx.setSender(sender);
await saveGeneratedTx(tx, 'Transferred Object', client, options);
} else {
await broadcast(client, keypair, tx, 'Transferred Object');
}
}

async function mainProcessor(options, processor) {
Expand All @@ -57,6 +63,9 @@ if (require.main === module) {
program.addOption(new Option('--objectId <objectId>', 'object id to be transferred'));
program.addOption(new Option('--objectName <objectName>', 'object name to be transferred'));
program.addOption(new Option('--recipient <recipient>', 'recipient to transfer object to').makeOptionMandatory(true));
program.addOption(new Option('--sender <sender>', 'transaction sender'));
program.addOption(new Option('--offline', 'store tx block for sign'));
program.addOption(new Option('--txFilePath <file>', 'unsigned transaction will be stored'));

program.action(async (options) => {
mainProcessor(options, processCommand);
Expand Down
Loading
Loading