From c948de9e694e8378b7f71a4609b8a7cb3e1b0f42 Mon Sep 17 00:00:00 2001 From: ahramy Date: Tue, 7 Jan 2025 13:08:14 -0800 Subject: [PATCH 1/6] feat(stellar): added its example scripts --- stellar/README.md | 27 ++++++++ stellar/gmp.js | 17 +---- stellar/its-example.js | 139 +++++++++++++++++++++++++++++++++++++++++ stellar/utils.js | 35 +++++++++++ 4 files changed, 202 insertions(+), 16 deletions(-) create mode 100644 stellar/its-example.js diff --git a/stellar/README.md b/stellar/README.md index 97ee6671..914aaa11 100644 --- a/stellar/README.md +++ b/stellar/README.md @@ -91,6 +91,7 @@ Deploy Interchain Token wasm first. ```bash node stellar/deploy-contract.js deploy interchain_token --chain-name --wasm-path ../axelar-cgp-soroban/target/wasm32-unknown-unknown/release/interchain_token.optimized.wasm + node stellar/deploy-contract.js deploy interchain_token_service --chain-name --wasm-path ../axelar-cgp-soroban/target/wasm32-unknown-unknown/release/interchain_token_service.optimized.wasm ``` @@ -244,3 +245,29 @@ node stellar/gmp.js [source-chain] [message-id] [source-address] [payload] # Example node stellar/gmp.js execute avalanche '0x0bcbbfc9b006db6958f3fce75f11fdc306b45e8e43396211f414f40d2d6db7c5-0' 0xba76c6980428A0b10CFC5d8ccb61949677A61233 0x1234 ``` + +### ITS Examples + +#### Deploy Interchain Token + +```bash +node stellar/its-example.js deploy-token [name] [symbol] [decimal] [salt] [initial-supply] +``` + +#### Deploy Remote Interchain Token + +```bash +node stellar/its-example.js deploy-remote-token [salt] [destination-chain] [gas-token-address] [gas-fee-amount] +``` + +#### Deploy Remote Interchain Token + +```bash +node stellar/its-example.js send-token [token-id] [destination-chain] [destination-address] [amount] [data] [gas-token-address] [gas-fee-amount] +``` + +#### Execute + +```bash +node stellar/its-example.js execute [source-chain] [message-id] [source-address] [payload] +``` diff --git a/stellar/gmp.js b/stellar/gmp.js index 7a3cee60..2b0e4bb0 100644 --- a/stellar/gmp.js +++ b/stellar/gmp.js @@ -3,7 +3,7 @@ const { Contract, Address, nativeToScVal } = require('@stellar/stellar-sdk'); const { Command } = require('commander'); const { loadConfig, printInfo, saveConfig } = require('../evm/utils'); -const { getWallet, broadcast, addBaseOptions } = require('./utils'); +const { getWallet, broadcast, addBaseOptions, tokenToScVal } = require('./utils'); const { addOptionsToCommands, getChainConfig } = require('../common'); const { ethers } = require('hardhat'); const { @@ -11,21 +11,6 @@ const { } = ethers; require('./cli-utils'); -function tokenToScVal(tokenAddress, tokenAmount) { - return nativeToScVal( - { - address: Address.fromString(tokenAddress), - amount: tokenAmount, - }, - { - type: { - address: ['symbol', 'address'], - amount: ['symbol', 'i128'], - }, - }, - ); -} - async function send(wallet, _, chain, contractConfig, args, options) { const contract = new Contract(contractConfig.address); const caller = nativeToScVal(Address.fromString(wallet.publicKey()), { type: 'address' }); diff --git a/stellar/its-example.js b/stellar/its-example.js new file mode 100644 index 00000000..58f20afd --- /dev/null +++ b/stellar/its-example.js @@ -0,0 +1,139 @@ +'use strict'; + +const { Contract, Address, nativeToScVal } = require('@stellar/stellar-sdk'); +const { Command } = require('commander'); +const { loadConfig, saveConfig } = require('../evm/utils'); +const { getWallet, broadcast, addBaseOptions, tokenToScVal, tokenMetadataToScVal } = require('./utils'); +const { addOptionsToCommands, getChainConfig } = require('../common'); +const { ethers } = require('hardhat'); +const { + utils: { arrayify, hexZeroPad }, +} = ethers; +require('./cli-utils'); + +async function deployToken(wallet, _, chain, contractConfig, args, options) { + const contract = new Contract(contractConfig.address); + const caller = nativeToScVal(Address.fromString(wallet.publicKey()), { type: 'address' }); + const minter = nativeToScVal(Address.fromString(wallet.publicKey()), { type: 'address' }); + const [symbol, name, decimal, salt, initialSupply] = args; + const saltBytes32 = hexZeroPad(salt.startsWith('0x') ? salt : '0x' + salt, 32); + + const operation = contract.call( + 'deploy_interchain_token', + caller, + nativeToScVal(Buffer.from(arrayify(saltBytes32)), { type: 'bytes' }), + tokenMetadataToScVal(decimal, name, symbol), + nativeToScVal(initialSupply, { type: 'i128' }), + minter, + ); + + await broadcast(operation, wallet, chain, 'Deploy Token', options); +} + +async function deployRemoteToken(wallet, _, chain, contractConfig, args, options) { + const contract = new Contract(contractConfig.address); + const caller = nativeToScVal(Address.fromString(wallet.publicKey()), { type: 'address' }); + const [salt, destinationChain, gasTokenAddress, gasFeeAmount] = args; + const saltBytes32 = hexZeroPad(salt.startsWith('0x') ? salt : '0x' + salt, 32); + + const operation = contract.call( + 'deploy_remote_interchain_token', + caller, + nativeToScVal(Buffer.from(arrayify(saltBytes32)), { type: 'bytes' }), + nativeToScVal(destinationChain, { type: 'string' }), + tokenToScVal(gasTokenAddress, gasFeeAmount), + ); + + await broadcast(operation, wallet, chain, 'Deploy Token', options); +} + +async function sendToken(wallet, _, chain, contractConfig, args, options) { + const contract = new Contract(contractConfig.address); + const caller = nativeToScVal(Address.fromString(wallet.publicKey()), { type: 'address' }); + const [tokenId, destinationChain, destinationAddress, amount, data, gasTokenAddress, gasFeeAmount] = args; + + const operation = contract.call( + 'interchain_transfer', + caller, + nativeToScVal(Buffer.from(arrayify(tokenId)), { type: 'bytes' }), + nativeToScVal(destinationChain, { type: 'string' }), + nativeToScVal(Buffer.from(arrayify(destinationAddress)), { type: 'bytes' }), + nativeToScVal(amount, { type: 'i128' }), + nativeToScVal(Buffer.from(arrayify(data)), { type: 'bytes' }), + tokenToScVal(gasTokenAddress, gasFeeAmount), + ); + + await broadcast(operation, wallet, chain, 'Send Token', options); +} + +async function execute(wallet, _, chain, contractConfig, args, options) { + const contract = new Contract(contractConfig.address); + const [sourceChain, messageId, sourceAddress, payload] = args; + + const operation = contract.call( + 'execute', + nativeToScVal(sourceChain, { type: 'string' }), + nativeToScVal(messageId, { type: 'string' }), + nativeToScVal(sourceAddress, { type: 'string' }), + nativeToScVal(Buffer.from(arrayify(payload)), { type: 'bytes' }), + ); + + await broadcast(operation, wallet, chain, 'Execute Called', options); +} + +async function mainProcessor(processor, args, options) { + const { yes } = options; + const config = loadConfig(options.env); + const chain = getChainConfig(config, options.chainName); + const wallet = await getWallet(chain, options); + + if (!chain.contracts?.interchain_token_service) { + throw new Error('Interchain Token Service package not found.'); + } + + await processor(wallet, config, chain, chain.contracts.interchain_token_service, args, options); + + saveConfig(config, options.env); +} + +if (require.main === module) { + const program = new Command(); + + program.name('its-example').description('Setllar ITS Example scripts.'); + + program + .name('deploy-token') + .description('deploy interchain token') + .command('deploy-token ') + .action((symbol, name, decimal, salt, initialSupply, options) => { + mainProcessor(deployToken, [symbol, name, decimal, salt, initialSupply], options); + }); + + program + .name('deploy-remote-token') + .description('deploy remote interchain token') + .command('deploy-remote-token ') + .action((salt, destinationChain, gasTokenAddress, gasFeeAmount, options) => { + mainProcessor(deployRemoteToken, [salt, destinationChain, gasTokenAddress, gasFeeAmount], options); + }); + + program + .name('send-token') + .description('send token') + .command('send-token ') + .action((tokenId, destinationChain, destinationAddress, amount, data, gasTokenAddress, gasFeeAmount, options) => { + mainProcessor(sendToken, [tokenId, destinationChain, destinationAddress, amount, data, gasTokenAddress, gasFeeAmount], options); + }); + + program + .name('execute') + .description('execute a message') + .command('execute ') + .action((sourceChain, messageId, sourceAddress, payload, options) => { + mainProcessor(execute, [sourceChain, messageId, sourceAddress, payload], options); + }); + + addOptionsToCommands(program, addBaseOptions); + + program.parse(); +} diff --git a/stellar/utils.js b/stellar/utils.js index 97290665..616aae32 100644 --- a/stellar/utils.js +++ b/stellar/utils.js @@ -10,6 +10,7 @@ const { xdr: { DiagnosticEvent, SorobanTransactionData }, Address, xdr, + nativeToScVal, } = require('@stellar/stellar-sdk'); const { printInfo, sleep, addEnvOption } = require('../common'); const { Option } = require('commander'); @@ -291,6 +292,38 @@ const createAuthorizedFunc = (contractAddress, functionName, args) => }), ); +function tokenToScVal(tokenAddress, tokenAmount) { + return nativeToScVal( + { + address: Address.fromString(tokenAddress), + amount: tokenAmount, + }, + { + type: { + address: ['symbol', 'address'], + amount: ['symbol', 'i128'], + }, + }, + ); +} + +function tokenMetadataToScVal(decimal, name, symbol) { + return nativeToScVal( + { + decimal, + name, + symbol, + }, + { + type: { + decimal: ['symbol', 'u32'], + name: ['symbol', 'string'], + symbol: ['symbol', 'string'], + }, + }, + ); +} + module.exports = { stellarCmd, ASSET_TYPE_NATIVE, @@ -306,4 +339,6 @@ module.exports = { serializeValue, getBalances, createAuthorizedFunc, + tokenToScVal, + tokenMetadataToScVal, }; From f6fb5ebd9f2223ea9a0d8c956ce7e8b6a7acb42e Mon Sep 17 00:00:00 2001 From: ahramy Date: Tue, 7 Jan 2025 13:43:49 -0800 Subject: [PATCH 2/6] Update its-example.js --- stellar/its-example.js | 1 - 1 file changed, 1 deletion(-) diff --git a/stellar/its-example.js b/stellar/its-example.js index 58f20afd..f779dbb8 100644 --- a/stellar/its-example.js +++ b/stellar/its-example.js @@ -82,7 +82,6 @@ async function execute(wallet, _, chain, contractConfig, args, options) { } async function mainProcessor(processor, args, options) { - const { yes } = options; const config = loadConfig(options.env); const chain = getChainConfig(config, options.chainName); const wallet = await getWallet(chain, options); From 643cf04e06c499f6fdbc41f81767b1faf36d81ed Mon Sep 17 00:00:00 2001 From: ahramy Date: Tue, 7 Jan 2025 15:27:03 -0800 Subject: [PATCH 3/6] Update README.md --- stellar/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stellar/README.md b/stellar/README.md index 914aaa11..81e7db04 100644 --- a/stellar/README.md +++ b/stellar/README.md @@ -260,7 +260,7 @@ node stellar/its-example.js deploy-token [name] [symbol] [decimal] [salt] [initi node stellar/its-example.js deploy-remote-token [salt] [destination-chain] [gas-token-address] [gas-fee-amount] ``` -#### Deploy Remote Interchain Token +#### Send Interchain Token ```bash node stellar/its-example.js send-token [token-id] [destination-chain] [destination-address] [amount] [data] [gas-token-address] [gas-fee-amount] From 6c65f8a95ae26fe6990f2f1faccc93098112586c Mon Sep 17 00:00:00 2001 From: ahramy Date: Wed, 8 Jan 2025 22:55:06 -0800 Subject: [PATCH 4/6] update --- stellar/README.md | 56 +++++++++-------- stellar/gateway.js | 22 +++++++ stellar/its-example.js | 138 ----------------------------------------- stellar/its.js | 137 +++++++++++++++++++++++++++++++++++++--- 4 files changed, 182 insertions(+), 171 deletions(-) delete mode 100644 stellar/its-example.js diff --git a/stellar/README.md b/stellar/README.md index 81e7db04..5d390ba8 100644 --- a/stellar/README.md +++ b/stellar/README.md @@ -199,6 +199,36 @@ node stellar/its.js set-trusted-chain [chain-name] node stellar/its.js remove-trusted-chain [chain-name] ``` +#### Deploy Interchain Token + +```bash +node stellar/its.js deploy-interchain-token [name] [symbol] [decimal] [salt] [initial-supply] +``` + +#### Deploy Remote Interchain Token + +```bash +node stellar/its.js deploy-remote-interchain-token [salt] [destination-chain] [gas-token-address] [gas-fee-amount] +``` + +#### Register Canonical Token + +```bash +node stellar/its.js register-canonical-token [token-address] +``` + +#### Interchain Transfer + +```bash +node stellar/its.js interchain-transfer [token-id] [destination-chain] [destination-address] [amount] [data] [gas-token-address] [gas-fee-amount] +``` + +#### Execute + +```bash +node stellar/its.js execute [source-chain] [message-id] [source-address] [payload] +``` + ## TTL extension and state archival recovery All Soroban storage entries, including contract instances, have a 'time to live' (`ttl`) after which entries will be archived and no longer accessible until restored. The following commands can be used to extend `ttl` or restore archived contract instances. @@ -245,29 +275,3 @@ node stellar/gmp.js [source-chain] [message-id] [source-address] [payload] # Example node stellar/gmp.js execute avalanche '0x0bcbbfc9b006db6958f3fce75f11fdc306b45e8e43396211f414f40d2d6db7c5-0' 0xba76c6980428A0b10CFC5d8ccb61949677A61233 0x1234 ``` - -### ITS Examples - -#### Deploy Interchain Token - -```bash -node stellar/its-example.js deploy-token [name] [symbol] [decimal] [salt] [initial-supply] -``` - -#### Deploy Remote Interchain Token - -```bash -node stellar/its-example.js deploy-remote-token [salt] [destination-chain] [gas-token-address] [gas-fee-amount] -``` - -#### Send Interchain Token - -```bash -node stellar/its-example.js send-token [token-id] [destination-chain] [destination-address] [amount] [data] [gas-token-address] [gas-fee-amount] -``` - -#### Execute - -```bash -node stellar/its-example.js execute [source-chain] [message-id] [source-address] [payload] -``` diff --git a/stellar/gateway.js b/stellar/gateway.js index 479e7711..64f93fc8 100644 --- a/stellar/gateway.js +++ b/stellar/gateway.js @@ -163,6 +163,21 @@ async function submitProof(wallet, config, chain, contractConfig, args, options) await broadcast(operation, wallet, chain, 'Amplifier Proof Submitted', options); } +async function execute(wallet, _, chain, contractConfig, args, options) { + const contract = new Contract(contractConfig.address); + const [sourceChain, messageId, sourceAddress, payload] = args; + + const operation = contract.call( + 'execute', + nativeToScVal(sourceChain, { type: 'string' }), + nativeToScVal(messageId, { type: 'string' }), + nativeToScVal(sourceAddress, { type: 'string' }), + nativeToScVal(Buffer.from(arrayify(payload)), { type: 'bytes' }), + ); + + await broadcast(operation, wallet, chain, 'Executed', options); +} + async function mainProcessor(processor, args, options) { const config = loadConfig(options.env); const chain = getChainConfig(config, options.chainName); @@ -223,6 +238,13 @@ if (require.main === module) { mainProcessor(callContract, [destinationChain, destinationAddress, payload], options); }); + program + .command('execute ') + .description('execute a message') + .action((sourceChain, messageId, sourceAddress, payload, options) => { + mainProcessor(execute, [sourceChain, messageId, sourceAddress, payload], options); + }); + addOptionsToCommands(program, addBaseOptions); program.parse(); diff --git a/stellar/its-example.js b/stellar/its-example.js deleted file mode 100644 index f779dbb8..00000000 --- a/stellar/its-example.js +++ /dev/null @@ -1,138 +0,0 @@ -'use strict'; - -const { Contract, Address, nativeToScVal } = require('@stellar/stellar-sdk'); -const { Command } = require('commander'); -const { loadConfig, saveConfig } = require('../evm/utils'); -const { getWallet, broadcast, addBaseOptions, tokenToScVal, tokenMetadataToScVal } = require('./utils'); -const { addOptionsToCommands, getChainConfig } = require('../common'); -const { ethers } = require('hardhat'); -const { - utils: { arrayify, hexZeroPad }, -} = ethers; -require('./cli-utils'); - -async function deployToken(wallet, _, chain, contractConfig, args, options) { - const contract = new Contract(contractConfig.address); - const caller = nativeToScVal(Address.fromString(wallet.publicKey()), { type: 'address' }); - const minter = nativeToScVal(Address.fromString(wallet.publicKey()), { type: 'address' }); - const [symbol, name, decimal, salt, initialSupply] = args; - const saltBytes32 = hexZeroPad(salt.startsWith('0x') ? salt : '0x' + salt, 32); - - const operation = contract.call( - 'deploy_interchain_token', - caller, - nativeToScVal(Buffer.from(arrayify(saltBytes32)), { type: 'bytes' }), - tokenMetadataToScVal(decimal, name, symbol), - nativeToScVal(initialSupply, { type: 'i128' }), - minter, - ); - - await broadcast(operation, wallet, chain, 'Deploy Token', options); -} - -async function deployRemoteToken(wallet, _, chain, contractConfig, args, options) { - const contract = new Contract(contractConfig.address); - const caller = nativeToScVal(Address.fromString(wallet.publicKey()), { type: 'address' }); - const [salt, destinationChain, gasTokenAddress, gasFeeAmount] = args; - const saltBytes32 = hexZeroPad(salt.startsWith('0x') ? salt : '0x' + salt, 32); - - const operation = contract.call( - 'deploy_remote_interchain_token', - caller, - nativeToScVal(Buffer.from(arrayify(saltBytes32)), { type: 'bytes' }), - nativeToScVal(destinationChain, { type: 'string' }), - tokenToScVal(gasTokenAddress, gasFeeAmount), - ); - - await broadcast(operation, wallet, chain, 'Deploy Token', options); -} - -async function sendToken(wallet, _, chain, contractConfig, args, options) { - const contract = new Contract(contractConfig.address); - const caller = nativeToScVal(Address.fromString(wallet.publicKey()), { type: 'address' }); - const [tokenId, destinationChain, destinationAddress, amount, data, gasTokenAddress, gasFeeAmount] = args; - - const operation = contract.call( - 'interchain_transfer', - caller, - nativeToScVal(Buffer.from(arrayify(tokenId)), { type: 'bytes' }), - nativeToScVal(destinationChain, { type: 'string' }), - nativeToScVal(Buffer.from(arrayify(destinationAddress)), { type: 'bytes' }), - nativeToScVal(amount, { type: 'i128' }), - nativeToScVal(Buffer.from(arrayify(data)), { type: 'bytes' }), - tokenToScVal(gasTokenAddress, gasFeeAmount), - ); - - await broadcast(operation, wallet, chain, 'Send Token', options); -} - -async function execute(wallet, _, chain, contractConfig, args, options) { - const contract = new Contract(contractConfig.address); - const [sourceChain, messageId, sourceAddress, payload] = args; - - const operation = contract.call( - 'execute', - nativeToScVal(sourceChain, { type: 'string' }), - nativeToScVal(messageId, { type: 'string' }), - nativeToScVal(sourceAddress, { type: 'string' }), - nativeToScVal(Buffer.from(arrayify(payload)), { type: 'bytes' }), - ); - - await broadcast(operation, wallet, chain, 'Execute Called', options); -} - -async function mainProcessor(processor, args, options) { - const config = loadConfig(options.env); - const chain = getChainConfig(config, options.chainName); - const wallet = await getWallet(chain, options); - - if (!chain.contracts?.interchain_token_service) { - throw new Error('Interchain Token Service package not found.'); - } - - await processor(wallet, config, chain, chain.contracts.interchain_token_service, args, options); - - saveConfig(config, options.env); -} - -if (require.main === module) { - const program = new Command(); - - program.name('its-example').description('Setllar ITS Example scripts.'); - - program - .name('deploy-token') - .description('deploy interchain token') - .command('deploy-token ') - .action((symbol, name, decimal, salt, initialSupply, options) => { - mainProcessor(deployToken, [symbol, name, decimal, salt, initialSupply], options); - }); - - program - .name('deploy-remote-token') - .description('deploy remote interchain token') - .command('deploy-remote-token ') - .action((salt, destinationChain, gasTokenAddress, gasFeeAmount, options) => { - mainProcessor(deployRemoteToken, [salt, destinationChain, gasTokenAddress, gasFeeAmount], options); - }); - - program - .name('send-token') - .description('send token') - .command('send-token ') - .action((tokenId, destinationChain, destinationAddress, amount, data, gasTokenAddress, gasFeeAmount, options) => { - mainProcessor(sendToken, [tokenId, destinationChain, destinationAddress, amount, data, gasTokenAddress, gasFeeAmount], options); - }); - - program - .name('execute') - .description('execute a message') - .command('execute ') - .action((sourceChain, messageId, sourceAddress, payload, options) => { - mainProcessor(execute, [sourceChain, messageId, sourceAddress, payload], options); - }); - - addOptionsToCommands(program, addBaseOptions); - - program.parse(); -} diff --git a/stellar/its.js b/stellar/its.js index 9ff21691..48e0fc10 100644 --- a/stellar/its.js +++ b/stellar/its.js @@ -1,12 +1,16 @@ const { Command } = require('commander'); -const { Contract, nativeToScVal } = require('@stellar/stellar-sdk'); +const { Contract, Address, nativeToScVal } = require('@stellar/stellar-sdk'); +const { ethers } = require('hardhat'); +const { + utils: { arrayify, hexZeroPad }, +} = ethers; const { saveConfig, loadConfig, addOptionsToCommands, getChainConfig } = require('../common'); -const { addBaseOptions, getWallet, broadcast } = require('./utils'); +const { addBaseOptions, getWallet, broadcast, tokenToScVal, tokenMetadataToScVal } = require('./utils'); const { prompt } = require('../common/utils'); -async function setTrustedChain(wallet, _, chain, contractConfig, arg, options) { - const contract = new Contract(contractConfig.address); +async function setTrustedChain(wallet, _, chain, arg, options) { + const contract = new Contract(chain.contracts.interchain_token_service?.address); const callArg = nativeToScVal(arg, { type: 'string' }); const operation = contract.call('set_trusted_chain', callArg); @@ -14,8 +18,8 @@ async function setTrustedChain(wallet, _, chain, contractConfig, arg, options) { await broadcast(operation, wallet, chain, 'Trusted Chain Set', options); } -async function removeTrustedChain(wallet, _, chain, contractConfig, arg, options) { - const contract = new Contract(contractConfig.address); +async function removeTrustedChain(wallet, _, chain, arg, options) { + const contract = new Contract(chain.contracts.interchain_token_service?.address); const callArg = nativeToScVal(arg, { type: 'string' }); const operation = contract.call('remove_trusted_chain', callArg); @@ -23,6 +27,86 @@ async function removeTrustedChain(wallet, _, chain, contractConfig, arg, options await broadcast(operation, wallet, chain, 'Trusted Chain Removed', options); } +async function deployInterchainToken(wallet, _, chain, args, options) { + const contract = new Contract(chain.contracts.interchain_token_service?.address); + const caller = nativeToScVal(Address.fromString(wallet.publicKey()), { type: 'address' }); + const minter = caller; + const [symbol, name, decimal, salt, initialSupply] = args; + const saltBytes32 = isHexString(salt) ? hexZeroPad(salt, 32) : keccak256(salt); + + const operation = contract.call( + 'deploy_interchain_token', + caller, + nativeToScVal(Buffer.from(arrayify(saltBytes32)), { type: 'bytes' }), + tokenMetadataToScVal(decimal, name, symbol), + nativeToScVal(initialSupply, { type: 'i128' }), + minter, + ); + + await broadcast(operation, wallet, chain, 'Interchain Token Deployed', options); +} + +async function deployRemoteInterchainToken(wallet, _, chain, args, options) { + const contract = new Contract(chain.contracts.interchain_token_service?.address); + const caller = nativeToScVal(Address.fromString(wallet.publicKey()), { type: 'address' }); + const [salt, destinationChain, gasTokenAddress, gasFeeAmount] = args; + const saltBytes32 = hexZeroPad(salt.startsWith('0x') ? salt : '0x' + salt, 32); + + const operation = contract.call( + 'deploy_remote_interchain_token', + caller, + nativeToScVal(Buffer.from(arrayify(saltBytes32)), { type: 'bytes' }), + nativeToScVal(destinationChain, { type: 'string' }), + tokenToScVal(gasTokenAddress, gasFeeAmount), + ); + + await broadcast(operation, wallet, chain, 'Remote Interchain Token Deployed', options); +} + +async function registerCanonicalToken(wallet, _, chain, args, options) { + const contract = new Contract(chain.contracts.interchain_token_service?.address); + const [tokenAddress] = args; + + const operation = contract.call('register_canonical_token', nativeToScVal(tokenAddress, { type: 'address' })); + + await broadcast(operation, wallet, chain, 'Canonical Token Registered', options); +} + +async function deployRemoteCanonicalToken(wallet, _, chain, args, options) { + const contract = new Contract(chain.contracts.interchain_token_service?.address); + const spender = nativeToScVal(Address.fromString(wallet.publicKey()), { type: 'address' }); + const [tokenAddress, destinationChain, gasTokenAddress, gasFeeAmount] = args; + + const operation = contract.call( + 'deploy_remote_canonical_token', + nativeToScVal(tokenAddress, { type: 'address' }), + nativeToScVal(destinationChain, { type: 'string' }), + spender, + tokenToScVal(gasTokenAddress, gasFeeAmount), + ); + + await broadcast(operation, wallet, chain, 'Remote Canonical Token Deployed', options); +} + +async function interchainTransfer(wallet, _, chain, args, options) { + const contract = new Contract(chain.contracts.interchain_token_service?.address); + const caller = nativeToScVal(Address.fromString(wallet.publicKey()), { type: 'address' }); + const [tokenId, destinationChain, destinationAddress, amount, data, gasTokenAddress, gasFeeAmount] = args; + + const operation = contract.call( + 'interchain_transfer', + caller, + nativeToScVal(Buffer.from(arrayify(tokenId)), { type: 'bytes' }), + nativeToScVal(destinationChain, { type: 'string' }), + nativeToScVal(Buffer.from(arrayify(destinationAddress)), { type: 'bytes' }), + nativeToScVal(amount, { type: 'i128' }), + nativeToScVal(Buffer.from(arrayify(data)), { type: 'bytes' }), + tokenToScVal(gasTokenAddress, gasFeeAmount), + ); + + await broadcast(operation, wallet, chain, 'Interchain Token Transferred', options); +} + async function mainProcessor(processor, args, options) { const { yes } = options; const config = loadConfig(options.env); @@ -37,7 +121,7 @@ async function mainProcessor(processor, args, options) { throw new Error('Interchain Token Service package not found.'); } - await processor(wallet, config, chain, chain.contracts.interchain_token_service, args, options); + await processor(wallet, config, chain, args, options); saveConfig(config, options.env); } @@ -61,6 +145,45 @@ if (require.main === module) { mainProcessor(removeTrustedChain, chainName, options); }); + program + .command('deploy-interchain-token ') + .description('deploy interchain token') + .action((symbol, name, decimal, salt, initialSupply, options) => { + mainProcessor(deployInterchainToken, [symbol, name, decimal, salt, initialSupply], options); + }); + + program + .command('deploy-remote-interchain-token ') + .description('deploy remote interchain token') + .action((salt, destinationChain, gasTokenAddress, gasFeeAmount, options) => { + mainProcessor(deployRemoteInterchainToken, [salt, destinationChain, gasTokenAddress, gasFeeAmount], options); + }); + + program + .command('register-canonical-token ') + .description('register canonical token') + .action((tokenAddress, options) => { + mainProcessor(registerCanonicalToken, [tokenAddress], options); + }); + + program + .command('deploy-remote-canonical-token ') + .description('deploy remote canonical token') + .action((tokenAddress, destinationChain, gasTokenAddress, gasFeeAmount, options) => { + mainProcessor(deployRemoteCanonicalToken, [tokenAddress, destinationChain, gasTokenAddress, gasFeeAmount], options); + }); + + program + .command('interchain-transfer ') + .description('interchain transfer') + .action((tokenId, destinationChain, destinationAddress, amount, data, gasTokenAddress, gasFeeAmount, options) => { + mainProcessor( + interchainTransfer, + [tokenId, destinationChain, destinationAddress, amount, data, gasTokenAddress, gasFeeAmount], + options, + ); + }); + addOptionsToCommands(program, addBaseOptions); program.parse(); From c7538b573f2ff56231f67bef90dd5b18481f9ebf Mon Sep 17 00:00:00 2001 From: ahramy Date: Thu, 9 Jan 2025 23:42:47 -0800 Subject: [PATCH 5/6] Update its.js --- stellar/its.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stellar/its.js b/stellar/its.js index 48e0fc10..b877c7d4 100644 --- a/stellar/its.js +++ b/stellar/its.js @@ -2,7 +2,7 @@ const { Command } = require('commander'); const { Contract, Address, nativeToScVal } = require('@stellar/stellar-sdk'); const { ethers } = require('hardhat'); const { - utils: { arrayify, hexZeroPad }, + utils: { arrayify, hexZeroPad, isHexString, keccak256 }, } = ethers; const { saveConfig, loadConfig, addOptionsToCommands, getChainConfig } = require('../common'); From 1d1040d90757f918d22fe1895d1dc73a7c4ffe2c Mon Sep 17 00:00:00 2001 From: ahramy Date: Fri, 10 Jan 2025 08:24:40 -0800 Subject: [PATCH 6/6] temporary PR update --- stellar/its.js | 94 ++++++++++++++++++++++++++++++++++++++++++------ stellar/utils.js | 3 ++ 2 files changed, 86 insertions(+), 11 deletions(-) diff --git a/stellar/its.js b/stellar/its.js index b877c7d4..44e6474b 100644 --- a/stellar/its.js +++ b/stellar/its.js @@ -1,12 +1,22 @@ +'use strict'; + +const { Address, Contract, nativeToScVal, Operation, xdr, authorizeInvocation, rpc } = require('@stellar/stellar-sdk'); const { Command } = require('commander'); -const { Contract, Address, nativeToScVal } = require('@stellar/stellar-sdk'); const { ethers } = require('hardhat'); const { utils: { arrayify, hexZeroPad, isHexString, keccak256 }, } = ethers; const { saveConfig, loadConfig, addOptionsToCommands, getChainConfig } = require('../common'); -const { addBaseOptions, getWallet, broadcast, tokenToScVal, tokenMetadataToScVal } = require('./utils'); +const { + addBaseOptions, + getWallet, + broadcast, + tokenToScVal, + tokenMetadataToScVal, + getNetworkPassphrase, + createAuthorizedFunc, +} = require('./utils'); const { prompt } = require('../common/utils'); async function setTrustedChain(wallet, _, chain, arg, options) { @@ -72,22 +82,84 @@ async function registerCanonicalToken(wallet, _, chain, args, options) { await broadcast(operation, wallet, chain, 'Canonical Token Registered', options); } +async function createAuth(itsAddress, gasServiceAddress, gatewayAddress, spenderScVal, gasTokenScVal, chain, wallet) { + const validUntil = await new rpc.Server(chain.rpc).getLatestLedger().then((info) => info.sequence + 100); + + const itsAddressScVal = nativeToScVal(Address.fromString(itsAddress), { type: 'address' }); + const axlearScVal = nativeToScVal('axelar', { type: 'string' }); + const axelarAddressScVal = nativeToScVal('axelar13ehuhysn5mqjeaheeuew2gjs785f6k7jm8vfsqg3jhtpkwppcmzqtedxty', { type: 'string' }); + const payloadHex = + '0x0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000096176616c616e636865000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000001c00a0cb91e6505241fd8c744cc5444fe3e0f5e18c551f5a3f09e9cfde4727a8100000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000066e6174697665000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066e617469766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'; + const emptyBytesScVal = nativeToScVal('()'); + + return Promise.all( + [ + createAuthorizedFunc(gasServiceAddress, 'pay_gas', [ + itsAddressScVal, + axlearScVal, + axelarAddressScVal, + nativeToScVal(Buffer.from(arrayify(payloadHex)), { type: 'bytes' }), + spenderScVal, + gasTokenScVal, + emptyBytesScVal, + ]), + createAuthorizedFunc(gatewayAddress, 'call_contract', [itsAddressScVal, axlearScVal, axelarAddressScVal, emptyBytesScVal]), + ].map((auth) => + authorizeInvocation( + wallet, + validUntil, + new xdr.SorobanAuthorizedInvocation({ + function: auth, + subInvocations: [], + }), + wallet.publicKey(), + getNetworkPassphrase(chain.networkType), + ), + ), + ); +} + async function deployRemoteCanonicalToken(wallet, _, chain, args, options) { - const contract = new Contract(chain.contracts.interchain_token_service?.address); - const spender = nativeToScVal(Address.fromString(wallet.publicKey()), { type: 'address' }); + let itsAddress = chain.contracts.interchain_token_service?.address; + let gasServiceAddress = chain.contracts.axelar_gas_service?.address; + let gatewayAddress = chain.contracts.axelar_gateway?.address; + + const spenderScVal = nativeToScVal(Address.fromString(wallet.publicKey()), { type: 'address' }); const [tokenAddress, destinationChain, gasTokenAddress, gasFeeAmount] = args; - const operation = contract.call( - 'deploy_remote_canonical_token', - nativeToScVal(tokenAddress, { type: 'address' }), - nativeToScVal(destinationChain, { type: 'string' }), - spender, - tokenToScVal(gasTokenAddress, gasFeeAmount), - ); + gasServiceAddress = Address.fromString(gasServiceAddress); + gatewayAddress = Address.fromString(gatewayAddress); + + let tokenAddressScVal = nativeToScVal(tokenAddress, { type: 'address' }); + let destinationChainsScVal = nativeToScVal(destinationChain, { type: 'string' }); + let gasTokenScVal = tokenToScVal(gasTokenAddress, gasFeeAmount); + + const operation = Operation.invokeContractFunction({ + contract: itsAddress, + function: 'deploy_remote_canonical_token', + args: [tokenAddressScVal, destinationChainsScVal, spenderScVal, gasTokenScVal], + auth: await createAuth(itsAddress, gasServiceAddress, gatewayAddress, spenderScVal, gasTokenScVal, chain, wallet), + }); await broadcast(operation, wallet, chain, 'Remote Canonical Token Deployed', options); } +// async function deployRemoteCanonicalTokenOrg(wallet, _, chain, args, options) { +// const contract = new Contract(chain.contracts.interchain_token_service?.address); +// const spender = nativeToScVal(Address.fromString(wallet.publicKey()), { type: 'address' }); +// const [tokenAddress, destinationChain, gasTokenAddress, gasFeeAmount] = args; + +// const operation = contract.call( +// 'deploy_remote_canonical_token', +// nativeToScVal(tokenAddress, { type: 'address' }), +// nativeToScVal(destinationChain, { type: 'string' }), +// spender, +// tokenToScVal(gasTokenAddress, gasFeeAmount), +// ); + +// await broadcast(operation, wallet, chain, 'Remote Canonical Token Deployed', options); +// } + async function interchainTransfer(wallet, _, chain, args, options) { const contract = new Contract(chain.contracts.interchain_token_service?.address); const caller = nativeToScVal(Address.fromString(wallet.publicKey()), { type: 'address' }); diff --git a/stellar/utils.js b/stellar/utils.js index 616aae32..e55aa4c6 100644 --- a/stellar/utils.js +++ b/stellar/utils.js @@ -78,6 +78,9 @@ async function buildTransaction(operation, server, wallet, networkType, options const prepareTransaction = async (operation, server, wallet, networkType, options = {}) => { const builtTransaction = await buildTransaction(operation, server, wallet, networkType, options); + // ahram test + printInfo('builtTransaction tx', builtTransaction); + // We use the RPC server to "prepare" the transaction. This simulating the // transaction, discovering the storage footprint, and updating the // transaction to include that footprint. If you know the footprint ahead of