Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
8 changes: 7 additions & 1 deletion .github/workflows/test-stellar.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ jobs:

- name: Deploy Remote Interchain Tokens
run: |
ts-node stellar/its.js deploy-interchain-token ${{ steps.env_vars.outputs.name }} ${{ steps.env_vars.outputs.symbol }} ${{ steps.env_vars.outputs.decimal }} ${{ steps.env_vars.outputs.salt }} ${{ steps.env_vars.outputs.amount }}
ts-node stellar/its.js deploy-interchain-token ${{ steps.env_vars.outputs.symbol }} ${{ steps.env_vars.outputs.name }} ${{ steps.env_vars.outputs.decimal }} ${{ steps.env_vars.outputs.salt }} ${{ steps.env_vars.outputs.amount }}
ts-node stellar/its.js deploy-remote-interchain-token ${{ steps.env_vars.outputs.salt }} ${{ steps.env_vars.outputs.destinationChain }}

###### Command: ITS - Create a Custom Token ######
Expand Down Expand Up @@ -257,6 +257,12 @@ jobs:
- name: ITS Flow Limit Remove
run: ts-node stellar/its.js remove-flow-limit ${{ steps.env_vars.outputs.tokenId }}

- name: ITS Freeze Token
run: ts-node stellar/its.js freeze-token ${{ steps.env_vars.outputs.tokenId }}

- name: ITS Unfreeze Token
run: ts-node stellar/its.js unfreeze-token ${{ steps.env_vars.outputs.tokenId }}

###### Command: Operators ######

- name: Operator
Expand Down
2 changes: 1 addition & 1 deletion axelar-chains-config/info/mainnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -2556,7 +2556,7 @@
"name": "Sui",
"axelarId": "sui",
"networkType": "mainnet",
"rpc": "https://sui-mainnet.gateway.tatum.io/",
"rpc": "https://fullnode.mainnet.sui.io",
"tokenSymbol": "SUI",
"decimals": 9,
"chainType": "sui",
Expand Down
2 changes: 1 addition & 1 deletion common/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ const isValidDecimal = (arg) => {

const num = parseFloat(arg);

return !isNaN(num) && isFinite(num) && num === parseFloat(String(arg).trim());
return !isNaN(num) && isFinite(num) && num === parseFloat(String(arg).trim()) && num >= 0;
};

const isNumberArray = (arr) => {
Expand Down
124 changes: 68 additions & 56 deletions evm/its.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
const { ethers } = require('hardhat');
const {
getDefaultProvider,
utils: { hexZeroPad, toUtf8Bytes, keccak256, parseUnits },
utils: { hexZeroPad, toUtf8Bytes, keccak256, parseUnits, formatUnits },
Contract,
} = ethers;
const { Command, Option, Argument } = require('commander');
Expand Down Expand Up @@ -95,6 +95,17 @@ async function handleTx(tx, chain, contract, action, firstEvent, secondEvent) {
}
}

async function tokenManagerAndMetadata(interchainTokenService, tokenId, wallet) {
const tokenManagerAddress = await interchainTokenService.deployedTokenManager(tokenId);
const tokenManager = new Contract(tokenManagerAddress, ITokenManager.abi, wallet);

const tokenAddress = await interchainTokenService.registeredTokenAddress(tokenId);
const token = new Contract(tokenAddress, getContractJSON('ERC20').abi, wallet);
const [symbol, decimals] = await Promise.all([token.symbol(), token.decimals()]);

return { tokenManager, tokenManagerAddress, tokenAddress, symbol, decimals };
}

async function getTrustedChains(chains, interchainTokenService, version) {
const chainIds = Object.values(chains)
.filter((chain) => chain.contracts.InterchainTokenService !== undefined)
Expand Down Expand Up @@ -260,37 +271,40 @@ async function processCommand(_axelar, chain, chains, action, options) {

validateTokenIds(interchainTokenService, [tokenId]);

const tokenManagerAddress = await interchainTokenService.deployedTokenManager(tokenId);
const tokenManager = new Contract(tokenManagerAddress, ITokenManager.abi, wallet);

const { tokenManager, symbol, decimals } = await tokenManagerAndMetadata(interchainTokenService, tokenId, wallet);
const flowLimit = await tokenManager.flowLimit();
printInfo(`Flow limit for tokenId ${tokenId}`, flowLimit);

if (flowLimit.isZero()) {
printInfo(`Flow limit for tokenId ${tokenId}`, 'No limit set');
} else {
printInfo(`Flow limit for tokenId ${tokenId}`, `${formatUnits(flowLimit, decimals)} ${symbol}`);
}

break;
}

case 'flow-out-amount': {
const [tokenId] = args;
validateTokenIds(interchainTokenService, [tokenId]);

const tokenManagerAddress = await interchainTokenService.deployedTokenManager(tokenId);
const tokenManager = new Contract(tokenManagerAddress, ITokenManager.abi, wallet);
validateTokenIds(interchainTokenService, [tokenId]);

const { tokenManager, symbol, decimals } = await tokenManagerAndMetadata(interchainTokenService, tokenId, wallet);
const flowOutAmount = await tokenManager.flowOutAmount();
printInfo(`Flow out amount for tokenId ${tokenId}`, flowOutAmount);

printInfo(`Flow out amount for tokenId ${tokenId}`, `${formatUnits(flowOutAmount, decimals)} ${symbol}`);

break;
}

case 'flow-in-amount': {
const [tokenId] = args;
validateTokenIds(interchainTokenService, [tokenId]);

const tokenManagerAddress = await interchainTokenService.deployedTokenManager(tokenId);
const tokenManager = new Contract(tokenManagerAddress, ITokenManager.abi, wallet);
validateTokenIds(interchainTokenService, [tokenId]);

const { tokenManager, symbol, decimals } = await tokenManagerAndMetadata(interchainTokenService, tokenId, wallet);
const flowInAmount = await tokenManager.flowInAmount();
printInfo(`Flow in amount for tokenId ${tokenId}`, flowInAmount);

printInfo(`Flow in amount for tokenId ${tokenId}`, `${formatUnits(flowInAmount, decimals)} ${symbol}`);

break;
}
Expand Down Expand Up @@ -344,29 +358,26 @@ async function processCommand(_axelar, chain, chains, action, options) {
});

const tokenIdBytes32 = hexZeroPad(tokenId.startsWith('0x') ? tokenId : '0x' + tokenId, 32);

const tokenManager = new Contract(
await interchainTokenService.deployedTokenManager(tokenIdBytes32),
getContractJSON('ITokenManager').abi,
wallet,
);
const token = new Contract(
await interchainTokenService.registeredTokenAddress(tokenIdBytes32),
getContractJSON('InterchainToken').abi,
const { tokenManager, tokenAddress, symbol, decimals } = await tokenManagerAndMetadata(
interchainTokenService,
tokenIdBytes32,
wallet,
);

await printTokenInfo(await interchainTokenService.registeredTokenAddress(tokenIdBytes32), provider);
const token = new Contract(tokenAddress, getContractJSON('InterchainToken').abi, wallet);

const implementationType = (await tokenManager.implementationType()).toNumber();
const decimals = await token.decimals();
const amountInUnits = parseUnits(amount, decimals);
const balance = await token.balanceOf(wallet.address);

if (balance.lt(amountInUnits)) {
throw new Error(`Insufficient balance for transfer. Balance: ${balance}, amount: ${amountInUnits}`);
throw new Error(
`Insufficient balance. Balance: ${formatUnits(balance, decimals)} ${symbol}, required: ${amount} ${symbol}`,
);
}

printInfo(`Token address`, tokenAddress);
printInfo(`Transfer amount`, `${amount} ${symbol}`);

if (implementationType !== tokenManagerTypes.MINT_BURN && implementationType !== tokenManagerTypes.NATIVE_INTERCHAIN_TOKEN) {
printInfo('Approving ITS for a transfer for token with token manager type', implementationType);
await token.approve(interchainTokenService.address, amountInUnits, gasOptions).then((tx) => tx.wait());
Expand Down Expand Up @@ -414,9 +425,14 @@ async function processCommand(_axelar, chain, chains, action, options) {
const [tokenId, flowLimit] = args;

validateTokenIds(interchainTokenService, [tokenId]);
validateParameters({ isValidNumber: { flowLimit } });
validateParameters({ isValidDecimal: { flowLimit } });
Comment thread
milapsheth marked this conversation as resolved.

const { tokenAddress, symbol, decimals } = await tokenManagerAndMetadata(interchainTokenService, tokenId, wallet);
await printTokenInfo(tokenAddress, provider);
const flowLimitInUnits = parseUnits(flowLimit, decimals);
printInfo(`Setting flow limit for tokenId ${tokenId}`, `${flowLimit} ${symbol}`);

const tx = await interchainTokenService.setFlowLimits([tokenId], [flowLimit], gasOptions);
const tx = await interchainTokenService.setFlowLimits([tokenId], [flowLimitInUnits], gasOptions);
await handleTx(tx, chain, interchainTokenService, action);
break;
}
Expand Down Expand Up @@ -483,9 +499,9 @@ async function processCommand(_axelar, chain, chains, action, options) {
validateParameters({ isNonEmptyString: { itsChain } });

if (await isTrustedChain(itsChain, interchainTokenService, itsVersion)) {
printInfo(`${itsChain} is a trusted chain`);
printInfo(`${itsChain}`, 'Trusted');
} else {
printInfo(`${itsChain} is not a trusted chain`);
printWarn(`${itsChain}`, 'Not trusted');
}

break;
Expand Down Expand Up @@ -764,20 +780,17 @@ async function processCommand(_axelar, chain, chains, action, options) {
validateParameters({ isValidTokenId: { tokenId }, isValidAddress: { to }, isValidNumber: { amount } });

const tokenIdBytes32 = hexZeroPad(tokenId.startsWith('0x') ? tokenId : '0x' + tokenId, 32);

// Get token manager address
const tokenManagerAddress = await interchainTokenService.deployedTokenManager(tokenIdBytes32);
const { tokenManager, tokenManagerAddress, tokenAddress, symbol, decimals } = await tokenManagerAndMetadata(
interchainTokenService,
tokenIdBytes32,
wallet,
);
printInfo(`TokenManager address for tokenId: ${tokenId}`, tokenManagerAddress);

// Get token address
const tokenAddress = await interchainTokenService.registeredTokenAddress(tokenIdBytes32);
printInfo(`Token address for tokenId: ${tokenId}`, tokenAddress);

const tokenManager = new Contract(tokenManagerAddress, ITokenManager.abi, wallet);

const amountInUnits = ethers.BigNumber.from(amount.toString());

if (prompt(`Proceed with minting ${amount} to ${to}?`, yes)) {
if (prompt(`Proceed with minting ${formatUnits(amountInUnits, decimals)} ${symbol} to ${to}?`, yes)) {
return;
}

Expand All @@ -793,22 +806,19 @@ async function processCommand(_axelar, chain, chains, action, options) {
validateParameters({ isValidTokenId: { tokenId }, isValidAddress: { spender }, isValidNumber: { amount } });

const tokenIdBytes32 = hexZeroPad(tokenId.startsWith('0x') ? tokenId : '0x' + tokenId, 32);

// Get token address
const tokenAddress = await interchainTokenService.registeredTokenAddress(tokenIdBytes32);
const { tokenAddress, symbol, decimals } = await tokenManagerAndMetadata(interchainTokenService, tokenIdBytes32, wallet);
printInfo(`Token address for tokenId: ${tokenId}`, tokenAddress);

// Create token contract instance
const token = new Contract(tokenAddress, getContractJSON('InterchainToken').abi, wallet);

const amountInUnits = ethers.BigNumber.from(amount.toString());
printInfo(`Approving ${spender} to spend ${amount} of token ${tokenId}`);
const formattedAmount = `${formatUnits(amountInUnits, decimals)} ${symbol}`;

if (prompt(`Proceed with approving ${spender} to spend ${amount}?`, yes)) {
printInfo(`Approving ${spender} to spend`, formattedAmount);

if (prompt(`Proceed with approving ${spender} to spend ${formattedAmount}?`, yes)) {
return;
}

// Execute approval
const tx = await token.approve(spender, amountInUnits, gasOptions);
await handleTx(tx, chain, token, action, 'Approval');

Expand Down Expand Up @@ -1038,7 +1048,7 @@ if (require.main === module) {
.command('set-flow-limit')
.description('Set flow limit for a token')
.argument('<token-id>', 'Token ID')
.argument('<flow-limit>', 'Flow limit')
.argument('<flow-limit>', 'Flow limit in token units (e.g. 100.5)')
.action((tokenId, flowLimit, options, cmd) => {
return main(cmd.name(), [tokenId, flowLimit], options);
});
Expand Down Expand Up @@ -1083,32 +1093,29 @@ if (require.main === module) {
return main(cmd.name(), [itsChain], options);
});

const setTrustedChainsCommand = program
const setTrustedChainsCmd = program
.command('set-trusted-chains')
.description('Set trusted chains')
.argument('<chains...>', 'Chains to trust')
.action((chains, options, cmd) => {
return main(cmd.name(), chains, options);
});
addGovernanceOptions(setTrustedChainsCommand);

const removeTrustedChainsCommand = program
const removeTrustedChainsCmd = program
.command('remove-trusted-chains')
.description('Remove trusted chains')
.argument('<chains...>', 'Chains to not trust')
.action((chains, options, cmd) => {
return main(cmd.name(), chains, options);
});
addGovernanceOptions(removeTrustedChainsCommand);

const setPauseStatusCommand = program
const setPauseStatusCmd = program
.command('set-pause-status')
.description('Set pause status')
.argument(new Argument('<pause-status>', 'Pause status (true/false)').choices(['true', 'false']))
.action((pauseStatus, options, cmd) => {
return main(cmd.name(), [pauseStatus], options);
});
addGovernanceOptions(setPauseStatusCommand);

program
.command('execute')
Expand All @@ -1128,14 +1135,13 @@ if (require.main === module) {
return main(cmd.name(), [], options);
});

const migrateInterchainTokenCommand = program
const migrateInterchainTokenCmd = program
.command('migrate-interchain-token')
.description('Migrate interchain token')
.argument('<token-id>', 'Token ID')
.action((tokenId, options, cmd) => {
return main(cmd.name(), [tokenId], options);
});
addGovernanceOptions(migrateInterchainTokenCommand);

program
.command('mint-token')
Expand Down Expand Up @@ -1182,6 +1188,12 @@ if (require.main === module) {

addOptionsToCommands(program, addEvmOptions, { address: true, salt: true });

// Add governance options only to commands that handle them
addGovernanceOptions(setTrustedChainsCmd);
addGovernanceOptions(removeTrustedChainsCmd);
addGovernanceOptions(setPauseStatusCmd);
addGovernanceOptions(migrateInterchainTokenCmd);

program.parseAsync().then(() => process.exit(0));
}

Expand Down
Loading
Loading